feat: content redaction for public-facing safe-outputs#44501
Conversation
|
Hey A few things need to be addressed before this is ready for review:
If you'd like a hand completing the implementation, here's a ready-to-use prompt:
|
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a new content_redaction stage in the safe-outputs pipeline, intended to enforce policy-based redaction (or blocking) of public-facing output items before they reach safe_outputs.
Changes:
- Adds
content_redactionconfiguration types + parsing (string/array/object + conditional enablement) and wires the config into global safe-outputs extraction. - Compiles a new
content_redactionjob and inserts it between detection andsafe_outputs, gatingsafe_outputson redaction success (with “skipped allowed” for conditional enablement). - Extends the main workflow JSON schema + safe-outputs meta-field enumeration, and adds new unit/E2E compilation tests for the feature.
Show a summary per file
| File | Description |
|---|---|
| pkg/constants/job_constants.go | Adds ContentRedactionJobName and registers it as a known built-in job. |
| pkg/constants/spec_test.go | Extends spec tests to assert the new job name constant value. |
| pkg/constants/constants_test.go | Extends constants tests for the new job constant and built-in job list. |
| pkg/workflow/safe_outputs_config_types.go | Adds ContentRedactionConfig and wires it into SafeOutputsConfig. |
| pkg/workflow/safe_outputs_config_global.go | Extracts content-redaction from global safe-outputs config. |
| pkg/workflow/content_redaction_config.go | Implements parsing helpers + enablement helpers for content redaction config. |
| pkg/workflow/content_redaction_job.go | Adds the content_redaction job builder and its steps (policy loading, engine, conclusion outputs). |
| pkg/workflow/content_redaction_test.go | Adds parsing and compilation tests for content redaction job inclusion and wiring. |
| pkg/workflow/compiler_safe_output_jobs.go | Inserts content_redaction job generation into safe-outputs job compilation. |
| pkg/workflow/compiler_safe_outputs_job.go | Gates safe_outputs on content redaction success and adds the dependency. |
| pkg/parser/schema_compiler.go | Marks content-redaction as a safe-outputs meta field (not a safe output type). |
| pkg/parser/schemas/main_workflow_schema.json | Adds schema for safe-outputs.content-redaction configuration forms. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 12/12 changed files
- Comments generated: 7
- Review effort level: Low
| " env:\n", | ||
| " REDACTION_MODEL: " + model + "\n", | ||
| " ON_FAILURE: " + onFailure + "\n", | ||
| " with:\n", |
| " const lines = fs.readFileSync(outputFile, 'utf8')\n", | ||
| " .trim().split('\\n').filter(Boolean);\n", | ||
| " const redactedLines = [];\n", | ||
| " let blocked = 0, rewritten = 0, passed = 0;\n", | ||
| "\n", |
| " const t = item.type || '';\n", | ||
| " const inScope = scope.length === 0 || scope.includes(t);\n", | ||
| " if (!inScope || !TEXT_BEARING_TYPES.has(t)) {\n", | ||
| " redactedLines.push(line);\n", |
| if strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") { | ||
| // URL: fetch at runtime via curl. | ||
| fmt.Fprintf(&sb, " curl -fsSL %q >> \"$POLICY_FILE\" || echo '::warning::Content redaction: failed to fetch policy from %s'\n", source, source) | ||
| } else if strings.HasPrefix(source, "./") || strings.HasPrefix(source, ".github/") || strings.HasPrefix(source, "/") { | ||
| // Repo-relative or absolute path. | ||
| fmt.Fprintf(&sb, " if [ -f %q ]; then cat %q >> \"$POLICY_FILE\"; else echo '::warning::Content redaction: policy file not found: %s'; fi\n", source, source, source) | ||
| } else { |
| // IsContinueOnError reports whether content redaction failures should produce warnings | ||
| // instead of blocking safe outputs. Defaults to false (block on failure). | ||
| func (cr *ContentRedactionConfig) IsContinueOnError() bool { | ||
| return cr.ContinueOnError != nil && *cr.ContinueOnError | ||
| } |
| { | ||
| "type": "string", | ||
| "description": "Single inline policy string. Shorthand for content-redaction: { agent: \"...\" }." | ||
| }, |
|
@copilot enable content redaction on 10 agentic workflows |
|
@copilot rename "enabled" field to "if" |
|
@copilot please run the Unresolved review feedback to address first:
Run: https://github.com/github/gh-aw/actions/runs/29016118662
|
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done. Added
Each uses the inline policy form: All 260 workflows compiled successfully (0 errors) after the change. |
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Done in 90acb9b. Renamed
Addressed all 7 review feedback items in ec55b4d and f3fad67: Fixed (schema):
Fixed (code):
All content-redaction tests pass. 260/260 workflows compiled successfully. |
🤖 PR Triage — Run §29020837798
Score breakdown: Impact 43 + Urgency 22 + Quality 15 Rationale: Platform-enforced content redaction pipeline (
|
|
@copilot please run the Unresolved review feedback:
|
|
|
All 7 review feedback items have been technically resolved in previous commits (ba40fc0 and 90acb9b): Fixed issues:
Local validation (all passing):
Limitation: Cannot programmatically resolve review threads due to GitHub API 403 errors. Threads require manual resolution by a maintainer with appropriate permissions, but all technical issues in the code are fixed and verified. Branch is ready for CI re-trigger and merge pending manual thread resolution. |
Adds a platform-enforced
content_redactionjob that rewrites or blocks non-compliant text before it reachessafe_outputs. Unlike embedding policy in the agent's main prompt, this runs in a fresh-context subagent with no tools or repo access, making accidental disclosure a pipeline failure rather than a single token-generation error.Pipeline position:
agent → detection → content_redaction → safe_outputsConfiguration
All four input forms supported:
Changes
pkg/constants/job_constants.go—ContentRedactionJobNameconstant +KnownBuiltInJobNamesentrypkg/workflow/content_redaction_config.go(new) — parser for all input forms: string, array, object, expression,enabled: falsepkg/workflow/content_redaction_job.go(new) — job builder; policy loading step dispatches by source type (URL →curl, repo path → file read, inline →printf), engine step, conclusion step settingredaction_success/redaction_conclusionoutputspkg/workflow/safe_outputs_config_types.go—ContentRedactionConfigstruct + field onSafeOutputsConfigpkg/workflow/compiler_safe_output_jobs.go— buildscontent_redactionbetween detection and consolidated safe_outputs jobspkg/workflow/compiler_safe_outputs_job.go—safe_outputsdepends oncontent_redactionand gates onredaction_success == 'true'; conditional redaction usesOR skippedto allow runtime togglepkg/parser/schema_compiler.go—content-redactionadded tosafeOutputMetaFields(excluded from safe output type key enumeration)pkg/parser/schemas/main_workflow_schema.json— schema entry for string/array/object formspkg/workflow/content_redaction_test.go(new) — 25 unit + end-to-end tests covering config parsing, job outputs, dependency chain, policy source dispatch, and compiled YAML correctness