diff --git a/internal/output/emitter.go b/internal/output/emitter.go index f0aa6b943..e840d0de4 100644 --- a/internal/output/emitter.go +++ b/internal/output/emitter.go @@ -39,17 +39,13 @@ type EmitterConfig struct { // envelope encoding and jq's complex-value encoding. Format is a canonical // typed value — boundaries reject unknown formats via ParseFormatStrict, so the // Emitter never sees one and never falls back. -// -// JQSafetyWarning preserves the legacy difference between RuntimeContext.emit -// (false) and WriteSuccessEnvelope (true) until their callers are migrated. type EmitOptions struct { - Raw bool - Meta *Meta - Format Format - JQ string - DryRun bool - Pretty PrettyRenderer - JQSafetyWarning bool + Raw bool + Meta *Meta + Format Format + JQ string + DryRun bool + Pretty PrettyRenderer } // StreamOptions describes one streamed page's wire representation. Streaming @@ -191,7 +187,7 @@ func (e *Emitter) emitEnvelope(data interface{}, ok bool, opts EmitOptions) erro } if opts.JQ != "" { - if scanResult.Alert != nil && opts.JQSafetyWarning { + if scanResult.Alert != nil { if err := WriteAlertWarning(e.errOut, scanResult.Alert); err != nil { return wrapOutputError("write", err) } diff --git a/internal/output/emitter_contract_test.go b/internal/output/emitter_contract_test.go index 21996a8d1..9f2eaa4e1 100644 --- a/internal/output/emitter_contract_test.go +++ b/internal/output/emitter_contract_test.go @@ -456,6 +456,41 @@ func TestEmitterRawJSONPropagatesWriteError(t *testing.T) { } } +func TestEmitterJQSafetyAlertAlwaysWritesStderrWarning(t *testing.T) { + t.Setenv("LARKSUITE_CLI_CONTENT_SAFETY_MODE", "warn") + extcs.Register(&contractSafetyProvider{alert: &extcs.Alert{ + Provider: "emitter-contract", + MatchedRules: []string{"fixture-rule"}, + }}) + t.Cleanup(func() { extcs.Register(nil) }) + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + emitter := output.NewEmitter(output.EmitterConfig{ + Out: stdout, + ErrOut: stderr, + CommandPath: "lark-cli fixture +emit", + }) + + err := emitter.Success(map[string]interface{}{"id": "1"}, output.EmitOptions{ + Format: output.FormatJSON, + JQ: ".data", + }) + if err != nil { + t.Fatalf("Emitter.Success() error = %v", err) + } + wantStdout := "{\n \"id\": \"1\"\n}\n" + if stdout.String() != wantStdout { + t.Fatalf("Emitter.Success() stdout = %q, want %q", stdout.String(), wantStdout) + } + if strings.Contains(stdout.String(), "_content_safety_alert") { + t.Fatalf("Emitter.Success() stdout contains filtered safety alert: %q", stdout.String()) + } + wantWarning := "warning: content safety alert from emitter-contract (rules: fixture-rule)\n" + if !strings.Contains(stderr.String(), wantWarning) { + t.Fatalf("Emitter.Success() stderr = %q, want warning containing %q", stderr.String(), wantWarning) + } +} + func TestEmitterInvalidJQReturnsErrorWithoutStderr(t *testing.T) { t.Setenv("LARKSUITE_CLI_CONTENT_SAFETY_MODE", "off") stderr := &bytes.Buffer{} diff --git a/internal/output/emitter_legacy_compat_test.go b/internal/output/emitter_legacy_compat_test.go index 672aeeb9f..b65ab411a 100644 --- a/internal/output/emitter_legacy_compat_test.go +++ b/internal/output/emitter_legacy_compat_test.go @@ -236,7 +236,7 @@ func TestEmitterMatchesRuntimeContextLegacyOracle(t *testing.T) { useFormat: true, }, { - name: "jq_safety_alert_without_stderr_warning", + name: "jq_safety_alert_writes_stderr_warning", data: func() interface{} { return map[string]interface{}{"id": "1"} }, @@ -539,11 +539,10 @@ func TestEmitterMatchesWriteSuccessEnvelopeLegacyOracle(t *testing.T) { Identity: "bot", NoticeProvider: func() map[string]interface{} { return notice }, }, true, output.EmitOptions{ - Format: output.FormatJSON, - Raw: false, - JQ: tc.jq, - DryRun: tc.dryRun, - JQSafetyWarning: true, + Format: output.FormatJSON, + Raw: false, + JQ: tc.jq, + DryRun: tc.dryRun, }) assertEmitterGolden(t, want, current) diff --git a/internal/output/envelope_success.go b/internal/output/envelope_success.go index 0ceb6ab01..b70f5840f 100644 --- a/internal/output/envelope_success.go +++ b/internal/output/envelope_success.go @@ -41,10 +41,9 @@ func WriteSuccessEnvelope(data interface{}, opts SuccessEnvelopeOptions) error { Identity: opts.Identity, NoticeProvider: GetNotice, }).Success(data, EmitOptions{ - Format: FormatJSON, - Raw: false, - JQ: opts.JqExpr, - DryRun: opts.DryRun, - JQSafetyWarning: true, + Format: FormatJSON, + Raw: false, + JQ: opts.JqExpr, + DryRun: opts.DryRun, }) } diff --git a/internal/output/testdata/runtime_context_legacy.golden.json b/internal/output/testdata/runtime_context_legacy.golden.json index baef7d738..17e572764 100644 --- a/internal/output/testdata/runtime_context_legacy.golden.json +++ b/internal/output/testdata/runtime_context_legacy.golden.json @@ -22,9 +22,9 @@ "exit_code": 2 } }, - "jq_safety_alert_without_stderr_warning": { + "jq_safety_alert_writes_stderr_warning": { "stdout": "1\n", - "stderr": "" + "stderr": "warning: content safety alert from emitter-oracle (rules: fixture-rule)\n" }, "jq_scalar": { "stdout": "Alice\n",