Go SDK: Align mode with SEP (#40)
<!-- Provide a brief summary of your changes --> ## Motivation and Context The SEP-2624 spec defines interceptor modes as "active" | "audit". This change aligns the Go SDK's interceptor `Mode` value with the SEP spec. ## How Has This Been Tested? - go test ./... from go/sdk — all packages pass (chain, extension, integrations/gomiddleware). - gofmt -l . — clean. - go vet ./... — clean. ## Breaking Changes Yes: - The exported constant interceptors.ModeEnforce is removed and replaced by interceptors.ModeActive. Callers referencing ModeEnforce must rename. - The serialized mode value changes from "enforce" to "active". Any config expecting "enforce" must be updated. ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [x] Breaking change (fix or feature that would cause existing functionality to change) - [x] Documentation update ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [x] New and existing tests pass locally - [ ] I have added appropriate error handling - [x] I have added or updated documentation as needed ## Additional context <!-- Add any other context, implementation notes, or design decisions --> Signed-off-by: monikaicampbell <mcampbell350@bloomberg.net>
This commit is contained in:
parent
39a8f0e073
commit
bd57572579
12 changed files with 64 additions and 64 deletions
|
|
@ -201,7 +201,7 @@ interface Interceptor {
|
||||||
/**
|
/**
|
||||||
* Failure routing policy (default: false - fail-closed)
|
* Failure routing policy (default: false - fail-closed)
|
||||||
*
|
*
|
||||||
* Enforce mode:
|
* Active mode:
|
||||||
* - false (fail-closed): If the interceptor crashes or times out, block the message
|
* - false (fail-closed): If the interceptor crashes or times out, block the message
|
||||||
* - true (fail-open): If the interceptor fails, allow the message to proceed
|
* - true (fail-open): If the interceptor fails, allow the message to proceed
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ ext.AddInterceptor(&interceptors.Validator{
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
raw := inv.Payload.(json.RawMessage)
|
raw := inv.Payload.(json.RawMessage)
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ Response-phase validators see the post-mutation payload.
|
||||||
|
|
||||||
### Validator execution
|
### Validator execution
|
||||||
- All matching validators run in parallel (goroutines).
|
- All matching validators run in parallel (goroutines).
|
||||||
- A validator returning `Valid: false` with `Severity: "error"` in enforced
|
- A validator returning `Valid: false` with `Severity: "error"` in active
|
||||||
mode aborts the chain.
|
mode aborts the chain.
|
||||||
- `FailOpen: true` validators log errors and record an `InvokeResult`
|
- `FailOpen: true` validators log errors and record an `InvokeResult`
|
||||||
for observability, but don't abort.
|
for observability, but don't abort.
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ func main() {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
raw, ok := inv.Payload.(json.RawMessage)
|
raw, ok := inv.Payload.(json.RawMessage)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ func main() {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
raw, ok := inv.Payload.(json.RawMessage)
|
raw, ok := inv.Payload.(json.RawMessage)
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ func TestChain_ExecutionHandler(t *testing.T) {
|
||||||
wantNoPayload bool
|
wantNoPayload bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "audit-to-enforce validator override aborts chain",
|
name: "audit-to-active validator override aborts chain",
|
||||||
interceptor: &interceptors.Validator{
|
interceptor: &interceptors.Validator{
|
||||||
Metadata: interceptors.Metadata{
|
Metadata: interceptors.Metadata{
|
||||||
Name: "v",
|
Name: "v",
|
||||||
|
|
@ -169,18 +169,18 @@ func TestChain_ExecutionHandler(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
directive: &chain.Directive{Mode: modePtr(interceptors.ModeEnforce)},
|
directive: &chain.Directive{Mode: modePtr(interceptors.ModeActive)},
|
||||||
phase: interceptors.PhaseRequest,
|
phase: interceptors.PhaseRequest,
|
||||||
wantStatus: chain.ChainValidationFailed,
|
wantStatus: chain.ChainValidationFailed,
|
||||||
wantAborted: true,
|
wantAborted: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "enforce-to-audit validator override does not abort",
|
name: "active-to-audit validator override does not abort",
|
||||||
interceptor: &interceptors.Validator{
|
interceptor: &interceptors.Validator{
|
||||||
Metadata: interceptors.Metadata{
|
Metadata: interceptors.Metadata{
|
||||||
Name: "v",
|
Name: "v",
|
||||||
Hooks: []interceptors.Hook{{Events: []string{"test/event"}, Phase: interceptors.PhaseRequest}},
|
Hooks: []interceptors.Hook{{Events: []string{"test/event"}, Phase: interceptors.PhaseRequest}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
return &interceptors.ValidationResult{
|
return &interceptors.ValidationResult{
|
||||||
|
|
@ -199,7 +199,7 @@ func TestChain_ExecutionHandler(t *testing.T) {
|
||||||
Metadata: interceptors.Metadata{
|
Metadata: interceptors.Metadata{
|
||||||
Name: "m",
|
Name: "m",
|
||||||
Hooks: []interceptors.Hook{{Events: []string{"test/event"}, Phase: interceptors.PhaseResponse}},
|
Hooks: []interceptors.Hook{{Events: []string{"test/event"}, Phase: interceptors.PhaseResponse}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
modified, _ := json.Marshal(map[string]any{"value": "mutated"})
|
modified, _ := json.Marshal(map[string]any{"value": "mutated"})
|
||||||
|
|
@ -217,7 +217,7 @@ func TestChain_ExecutionHandler(t *testing.T) {
|
||||||
Metadata: interceptors.Metadata{
|
Metadata: interceptors.Metadata{
|
||||||
Name: "v",
|
Name: "v",
|
||||||
Hooks: []interceptors.Hook{{Events: []string{"test/event"}, Phase: interceptors.PhaseRequest}},
|
Hooks: []interceptors.Hook{{Events: []string{"test/event"}, Phase: interceptors.PhaseRequest}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
return &interceptors.ValidationResult{
|
return &interceptors.ValidationResult{
|
||||||
|
|
@ -275,7 +275,7 @@ func TestChain_ExecutionHandler_ShortCircuit(t *testing.T) {
|
||||||
Metadata: interceptors.Metadata{
|
Metadata: interceptors.Metadata{
|
||||||
Name: "v",
|
Name: "v",
|
||||||
Hooks: []interceptors.Hook{{Events: []string{"test/event"}, Phase: interceptors.PhaseRequest}},
|
Hooks: []interceptors.Hook{{Events: []string{"test/event"}, Phase: interceptors.PhaseRequest}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
invoked = true
|
invoked = true
|
||||||
|
|
@ -320,7 +320,7 @@ func TestChain_FailOpenRecordsExecutionResult(t *testing.T) {
|
||||||
Events: []string{"test/event"},
|
Events: []string{"test/event"},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
FailOpen: true,
|
FailOpen: true,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
|
|
@ -334,7 +334,7 @@ func TestChain_FailOpenRecordsExecutionResult(t *testing.T) {
|
||||||
Events: []string{"test/event"},
|
Events: []string{"test/event"},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
return &interceptors.ValidationResult{Valid: true}, nil
|
return &interceptors.ValidationResult{Valid: true}, nil
|
||||||
|
|
@ -368,7 +368,7 @@ func TestChain_FailOpenRecordsExecutionResult(t *testing.T) {
|
||||||
Events: []string{"test/event"},
|
Events: []string{"test/event"},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
FailOpen: true,
|
FailOpen: true,
|
||||||
PriorityHint: interceptors.NewPriority(10),
|
PriorityHint: interceptors.NewPriority(10),
|
||||||
},
|
},
|
||||||
|
|
@ -383,7 +383,7 @@ func TestChain_FailOpenRecordsExecutionResult(t *testing.T) {
|
||||||
Events: []string{"test/event"},
|
Events: []string{"test/event"},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
PriorityHint: interceptors.NewPriority(20),
|
PriorityHint: interceptors.NewPriority(20),
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
|
|
@ -468,7 +468,7 @@ func TestChain_AuditModeErrorsDoNotAbort(t *testing.T) {
|
||||||
Events: []string{"test/event"},
|
Events: []string{"test/event"},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
PriorityHint: interceptors.NewPriority(20),
|
PriorityHint: interceptors.NewPriority(20),
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
//
|
//
|
||||||
// A [Validator] inspects the payload and decides whether the request
|
// A [Validator] inspects the payload and decides whether the request
|
||||||
// or response should proceed. All validators for a given event run
|
// or response should proceed. All validators for a given event run
|
||||||
// in parallel. If any validator in enforced mode ([ModeEnforce])
|
// in parallel. If any validator in active mode ([ModeActive])
|
||||||
// returns an error-severity message, the chain aborts before any
|
// returns an error-severity message, the chain aborts before any
|
||||||
// mutators run. Only error-severity messages cause an abort; warn
|
// mutators run. Only error-severity messages cause an abort; warn
|
||||||
// and info findings are recorded in the chain execution result but
|
// and info findings are recorded in the chain execution result but
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
// Events: []string{"tools/call"},
|
// Events: []string{"tools/call"},
|
||||||
// Phase: interceptors.PhaseRequest,
|
// Phase: interceptors.PhaseRequest,
|
||||||
// }},
|
// }},
|
||||||
// Mode: interceptors.ModeEnforce,
|
// Mode: interceptors.ModeActive,
|
||||||
// },
|
// },
|
||||||
// Handler: func(ctx context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
// Handler: func(ctx context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
// raw := inv.Payload.(json.RawMessage)
|
// raw := inv.Payload.(json.RawMessage)
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
// Events: []string{"tools/call"},
|
// Events: []string{"tools/call"},
|
||||||
// Phase: interceptors.PhaseResponse,
|
// Phase: interceptors.PhaseResponse,
|
||||||
// }},
|
// }},
|
||||||
// Mode: interceptors.ModeEnforce,
|
// Mode: interceptors.ModeActive,
|
||||||
// },
|
// },
|
||||||
// Handler: func(ctx context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
// Handler: func(ctx context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
// raw := inv.Payload.(json.RawMessage)
|
// raw := inv.Payload.(json.RawMessage)
|
||||||
|
|
@ -112,7 +112,7 @@
|
||||||
// successful results, and a FailOpen flag that controls what happens
|
// successful results, and a FailOpen flag that controls what happens
|
||||||
// when the handler returns a Go error. These are orthogonal:
|
// when the handler returns a Go error. These are orthogonal:
|
||||||
//
|
//
|
||||||
// - [ModeEnforce]: fully enforced — validation failures block,
|
// - [ModeActive]: fully active — validation failures block,
|
||||||
// mutations are applied.
|
// mutations are applied.
|
||||||
// - [ModeAudit]: the handler runs and results are recorded, but
|
// - [ModeAudit]: the handler runs and results are recorded, but
|
||||||
// validation findings do not block and mutated payloads are
|
// validation findings do not block and mutated payloads are
|
||||||
|
|
@ -138,15 +138,15 @@
|
||||||
//
|
//
|
||||||
// Behavior matrix for validators:
|
// Behavior matrix for validators:
|
||||||
//
|
//
|
||||||
// Mode=Enforce, FailOpen=false → error aborts, Valid=false+SeverityError aborts
|
// Mode=Active, FailOpen=false → error aborts, Valid=false+SeverityError aborts
|
||||||
// Mode=Enforce, FailOpen=true → error continues, Valid=false+SeverityError aborts
|
// Mode=Active, FailOpen=true → error continues, Valid=false+SeverityError aborts
|
||||||
// Mode=Audit, FailOpen=false → error aborts, findings recorded only
|
// Mode=Audit, FailOpen=false → error aborts, findings recorded only
|
||||||
// Mode=Audit, FailOpen=true → error continues, findings recorded only
|
// Mode=Audit, FailOpen=true → error continues, findings recorded only
|
||||||
//
|
//
|
||||||
// Behavior matrix for mutators:
|
// Behavior matrix for mutators:
|
||||||
//
|
//
|
||||||
// Mode=Enforce, FailOpen=false → error aborts, mutations applied
|
// Mode=Active, FailOpen=false → error aborts, mutations applied
|
||||||
// Mode=Enforce, FailOpen=true → error continues, mutations applied
|
// Mode=Active, FailOpen=true → error continues, mutations applied
|
||||||
// Mode=Audit, FailOpen=false → error aborts, mutations not propagated
|
// Mode=Audit, FailOpen=false → error aborts, mutations not propagated
|
||||||
// Mode=Audit, FailOpen=true → error continues, mutations not propagated
|
// Mode=Audit, FailOpen=true → error continues, mutations not propagated
|
||||||
package interceptors
|
package interceptors
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func TestListWithEventFilter(t *testing.T) {
|
||||||
Events: []string{interceptors.EventPromptsGet},
|
Events: []string{interceptors.EventPromptsGet},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
return &interceptors.ValidationResult{Valid: true}, nil
|
return &interceptors.ValidationResult{Valid: true}, nil
|
||||||
|
|
@ -122,7 +122,7 @@ func TestInvokeValidatorRejects(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
return &interceptors.ValidationResult{
|
return &interceptors.ValidationResult{
|
||||||
|
|
@ -166,7 +166,7 @@ func TestInvokeMutator(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
raw, ok := inv.Payload.(json.RawMessage)
|
raw, ok := inv.Payload.(json.RawMessage)
|
||||||
|
|
@ -244,7 +244,7 @@ func TestInvokeTimeout(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(ctx context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(ctx context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
select {
|
select {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func TestServer_ValidatorAllowsTool(t *testing.T) {
|
||||||
|
|
||||||
func TestServer_MutatorModifiesResponse(t *testing.T) {
|
func TestServer_MutatorModifiesResponse(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
cs := setup(t, prefixMutator("prefix", "[mutated]", interceptors.PhaseResponse, 0, interceptors.ModeEnforce))
|
cs := setup(t, prefixMutator("prefix", "[mutated]", interceptors.PhaseResponse, 0, interceptors.ModeActive))
|
||||||
|
|
||||||
result, err := callEcho(t, cs)
|
result, err := callEcho(t, cs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -50,11 +50,11 @@ func TestServer_ChainedMutatorsWithAuditMode(t *testing.T) {
|
||||||
//
|
//
|
||||||
// [M5] [M4] [M2] [M1] echo: ...
|
// [M5] [M4] [M2] [M1] echo: ...
|
||||||
cs := setup(t,
|
cs := setup(t,
|
||||||
prefixMutator("m1", "[M1]", interceptors.PhaseResponse, 10, interceptors.ModeEnforce),
|
prefixMutator("m1", "[M1]", interceptors.PhaseResponse, 10, interceptors.ModeActive),
|
||||||
prefixMutator("m2", "[M2]", interceptors.PhaseResponse, 20, interceptors.ModeEnforce),
|
prefixMutator("m2", "[M2]", interceptors.PhaseResponse, 20, interceptors.ModeActive),
|
||||||
prefixMutator("m3-audit", "[AUDIT]", interceptors.PhaseResponse, 30, interceptors.ModeAudit),
|
prefixMutator("m3-audit", "[AUDIT]", interceptors.PhaseResponse, 30, interceptors.ModeAudit),
|
||||||
prefixMutator("m4", "[M4]", interceptors.PhaseResponse, 40, interceptors.ModeEnforce),
|
prefixMutator("m4", "[M4]", interceptors.PhaseResponse, 40, interceptors.ModeActive),
|
||||||
prefixMutator("m5", "[M5]", interceptors.PhaseResponse, 50, interceptors.ModeEnforce),
|
prefixMutator("m5", "[M5]", interceptors.PhaseResponse, 50, interceptors.ModeActive),
|
||||||
)
|
)
|
||||||
|
|
||||||
result, err := callEcho(t, cs)
|
result, err := callEcho(t, cs)
|
||||||
|
|
@ -70,9 +70,9 @@ func TestServer_MutatorFailureAbortsChain(t *testing.T) {
|
||||||
// 3 response mutators. Mutator 2 (priority 20) returns an error
|
// 3 response mutators. Mutator 2 (priority 20) returns an error
|
||||||
// and is fail-closed. The chain aborts; client receives an error.
|
// and is fail-closed. The chain aborts; client receives an error.
|
||||||
cs := setup(t,
|
cs := setup(t,
|
||||||
prefixMutator("m1", "[M1]", interceptors.PhaseResponse, 10, interceptors.ModeEnforce),
|
prefixMutator("m1", "[M1]", interceptors.PhaseResponse, 10, interceptors.ModeActive),
|
||||||
failMutator("m2-fail", interceptors.PhaseResponse, 20),
|
failMutator("m2-fail", interceptors.PhaseResponse, 20),
|
||||||
prefixMutator("m3", "[M3]", interceptors.PhaseResponse, 30, interceptors.ModeEnforce),
|
prefixMutator("m3", "[M3]", interceptors.PhaseResponse, 30, interceptors.ModeActive),
|
||||||
)
|
)
|
||||||
|
|
||||||
_, err := callEcho(t, cs)
|
_, err := callEcho(t, cs)
|
||||||
|
|
@ -90,7 +90,7 @@ func TestServer_ValidatorWarnDoesNotBlock(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
return &interceptors.ValidationResult{
|
return &interceptors.ValidationResult{
|
||||||
|
|
@ -121,7 +121,7 @@ func TestServer_ValidationFailurePreventsM(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
mutatorRan.Store(true)
|
mutatorRan.Store(true)
|
||||||
|
|
@ -145,7 +145,7 @@ func TestServer_ValidatorTimeoutAbortsChain(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(ctx context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(ctx context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
select {
|
select {
|
||||||
|
|
@ -176,7 +176,7 @@ func TestServer_MutatorTimeoutAbortsChain(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
PriorityHint: interceptors.NewPriority(20),
|
PriorityHint: interceptors.NewPriority(20),
|
||||||
},
|
},
|
||||||
Handler: func(ctx context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(ctx context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
|
|
@ -190,9 +190,9 @@ func TestServer_MutatorTimeoutAbortsChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cs := setup(t,
|
cs := setup(t,
|
||||||
prefixMutator("m1", "[M1]", interceptors.PhaseResponse, 10, interceptors.ModeEnforce),
|
prefixMutator("m1", "[M1]", interceptors.PhaseResponse, 10, interceptors.ModeActive),
|
||||||
slowMutator,
|
slowMutator,
|
||||||
prefixMutator("m3", "[M3]", interceptors.PhaseResponse, 30, interceptors.ModeEnforce),
|
prefixMutator("m3", "[M3]", interceptors.PhaseResponse, 30, interceptors.ModeActive),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Per-invocation timeout: context expires before the 100ms handler completes.
|
// Per-invocation timeout: context expires before the 100ms handler completes.
|
||||||
|
|
@ -212,7 +212,7 @@ func TestServer_MutatorFailOpenContinuesChain(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
FailOpen: true,
|
FailOpen: true,
|
||||||
PriorityHint: interceptors.NewPriority(20),
|
PriorityHint: interceptors.NewPriority(20),
|
||||||
},
|
},
|
||||||
|
|
@ -222,9 +222,9 @@ func TestServer_MutatorFailOpenContinuesChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cs := setup(t,
|
cs := setup(t,
|
||||||
prefixMutator("m1", "[M1]", interceptors.PhaseResponse, 10, interceptors.ModeEnforce),
|
prefixMutator("m1", "[M1]", interceptors.PhaseResponse, 10, interceptors.ModeActive),
|
||||||
failOpenMutator,
|
failOpenMutator,
|
||||||
prefixMutator("m3", "[M3]", interceptors.PhaseResponse, 30, interceptors.ModeEnforce),
|
prefixMutator("m3", "[M3]", interceptors.PhaseResponse, 30, interceptors.ModeActive),
|
||||||
)
|
)
|
||||||
|
|
||||||
result, err := callEcho(t, cs)
|
result, err := callEcho(t, cs)
|
||||||
|
|
@ -252,7 +252,7 @@ func TestServer_ResponseMutatorSeesMutatedParams(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
raw, ok := inv.Payload.(json.RawMessage)
|
raw, ok := inv.Payload.(json.RawMessage)
|
||||||
|
|
@ -313,7 +313,7 @@ func TestServer_CombinedValidatorsAndMutators(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
reqValCount.Add(1)
|
reqValCount.Add(1)
|
||||||
|
|
@ -347,7 +347,7 @@ func TestServer_CombinedValidatorsAndMutators(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
reqValCount.Add(1)
|
reqValCount.Add(1)
|
||||||
|
|
@ -385,7 +385,7 @@ func TestServer_CombinedValidatorsAndMutators(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
reqValCount.Add(1)
|
reqValCount.Add(1)
|
||||||
|
|
@ -409,7 +409,7 @@ func TestServer_CombinedValidatorsAndMutators(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
PriorityHint: interceptors.NewPriority(priority),
|
PriorityHint: interceptors.NewPriority(priority),
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
|
|
@ -458,7 +458,7 @@ func TestServer_CombinedValidatorsAndMutators(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
PriorityHint: interceptors.NewPriority(priority),
|
PriorityHint: interceptors.NewPriority(priority),
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
|
|
@ -500,7 +500,7 @@ func TestServer_CombinedValidatorsAndMutators(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
respValCount.Add(1)
|
respValCount.Add(1)
|
||||||
|
|
@ -534,7 +534,7 @@ func TestServer_CombinedValidatorsAndMutators(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
respValCount.Add(1)
|
respValCount.Add(1)
|
||||||
|
|
@ -570,7 +570,7 @@ func TestServer_CombinedValidatorsAndMutators(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
respValCount.Add(1)
|
respValCount.Add(1)
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@ func failMutator(name string, phase interceptors.InterceptionPhase, priority int
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: phase,
|
Phase: phase,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
PriorityHint: interceptors.NewPriority(priority),
|
PriorityHint: interceptors.NewPriority(priority),
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
|
|
@ -249,7 +249,7 @@ func blockToolValidator(toolName string) *interceptors.Validator {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
raw, ok := inv.Payload.(json.RawMessage)
|
raw, ok := inv.Payload.(json.RawMessage)
|
||||||
|
|
@ -284,7 +284,7 @@ func allowAllValidator(name string) *interceptors.Validator {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
return &interceptors.ValidationResult{Valid: true}, nil
|
return &interceptors.ValidationResult{Valid: true}, nil
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ func TestMiddlewareBlocksOnValidationError(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
raw, ok := inv.Payload.(json.RawMessage)
|
raw, ok := inv.Payload.(json.RawMessage)
|
||||||
|
|
@ -129,7 +129,7 @@ func TestMiddlewarePassesOnSuccess(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, _ *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
return &interceptors.ValidationResult{Valid: true}, nil
|
return &interceptors.ValidationResult{Valid: true}, nil
|
||||||
|
|
@ -156,7 +156,7 @@ func TestMiddlewareMutatesPayload(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseResponse,
|
Phase: interceptors.PhaseResponse,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
raw, ok := inv.Payload.(json.RawMessage)
|
raw, ok := inv.Payload.(json.RawMessage)
|
||||||
|
|
@ -234,7 +234,7 @@ func TestMiddlewareWithContextProvider(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.ValidationResult, error) {
|
||||||
if inv.Context == nil || inv.Context.Principal == nil || inv.Context.Principal.ID != "test-user" {
|
if inv.Context == nil || inv.Context.Principal == nil || inv.Context.Principal.ID != "test-user" {
|
||||||
|
|
@ -316,7 +316,7 @@ func TestMiddlewareRequestMutatorModifiesArgs(t *testing.T) {
|
||||||
Events: []string{interceptors.EventToolsCall},
|
Events: []string{interceptors.EventToolsCall},
|
||||||
Phase: interceptors.PhaseRequest,
|
Phase: interceptors.PhaseRequest,
|
||||||
}},
|
}},
|
||||||
Mode: interceptors.ModeEnforce,
|
Mode: interceptors.ModeActive,
|
||||||
},
|
},
|
||||||
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
Handler: func(_ context.Context, inv *interceptors.Invocation) (*interceptors.MutationResult, error) {
|
||||||
raw, ok := inv.Payload.(json.RawMessage)
|
raw, ok := inv.Payload.(json.RawMessage)
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ type InterceptionEvent = string
|
||||||
type Mode string
|
type Mode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ModeEnforce Mode = "enforce" // Enforced: validation failures block, mutations apply
|
ModeActive Mode = "active" // Active: validation failures block, mutations apply
|
||||||
ModeAudit Mode = "audit" // Audit: log results but don't block or apply mutations
|
ModeAudit Mode = "audit" // Audit: log results but don't block or apply mutations
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue