mirror of
https://github.com/larksuite/cli.git
synced 2026-08-03 08:32:46 +08:00
refactor(output): always warn on stderr when jq may drop a content-safety alert
When --jq is applied, the jq expression can filter the _content_safety_alert field out of stdout, hiding the warning. Whether a stderr fallback warning was written used to depend on a caller-set EmitOptions.JQSafetyWarning flag, so the raw api/service paths warned but shortcut commands did not — the safety alert was silently lost. Remove the flag and always write the stderr warning when jq is applied and an alert exists.
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user