Skip to content

fix: wire detection gate for safe-outputs assembled entirely from imports#24155

Merged
pelikhan merged 5 commits intomainfrom
copilot/fix-invisible-char-sanitization
Apr 2, 2026
Merged

fix: wire detection gate for safe-outputs assembled entirely from imports#24155
pelikhan merged 5 commits intomainfrom
copilot/fix-invisible-char-sanitization

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

Workflows that have no safe-outputs: in their own frontmatter but pull it in via imports: compiled to a safe_outputs job with needs: agent only — no detection job, no detection.result == 'success' gate. The "automatically enabled when safe outputs are configured" guarantee did not hold for this path.

Root cause

extractSafeOutputsConfig applies the default ThreatDetection = &ThreatDetectionConfig{} only when parsing the main frontmatter's safe-outputs: block. When SafeOutputs is instead assembled by MergeSafeOutputs from imported configs, ThreatDetection stayed nil, so IsDetectionJobEnabled returned false.

Changes

  • pkg/workflow/compiler_orchestrator_workflow.go — after MergeSafeOutputs, when the original safeOutputs from frontmatter was nil (no safe-outputs: in main workflow) but the merged result is non-nil, apply the same default: ThreatDetection = &ThreatDetectionConfig{}. Skipped when any imported config explicitly disabled detection.

  • pkg/workflow/threat_detection.go — added isThreatDetectionExplicitlyDisabledInConfigs([]string) bool helper that scans raw imported config JSON for an explicit disable, supporting both the boolean form (threat-detection: false) and the object form (threat-detection: { enabled: false }), mirroring parseThreatDetectionConfig's syntax handling. This ensures the default is not applied when the import intentionally disables detection.

  • 31 compiled .lock.yml files regenerated — these are the real-world workflows (e.g. blog-auditor, copilot-session-insights) that imported a shared safe-outputs config without their own frontmatter block and now get a detection job and the proper needs.detection.result == 'success' condition on safe_outputs.

  • pkg/workflow/safe_jobs_threat_detection_test.go — five new tests:

    • TestIsThreatDetectionExplicitlyDisabledInConfigs — unit tests for the helper, including both boolean and object-form disable cases
    • TestDefaultThreatDetectionAppliedWhenSafeOutputsFromImportsOnly — detection enabled by default when imports provide safe-outputs
    • TestDefaultThreatDetectionNotAppliedWhenImportedConfigExplicitlyDisablesthreat-detection: false in an imported config is respected
    • TestDefaultThreatDetectionNotAppliedWhenImportedConfigObjectFormDisablesthreat-detection: { enabled: false } object form in an imported config is respected
    • TestImportedSafeOutputsCompiledWithDetectionJob — end-to-end: compiled lock file contains a detection: job and safe_outputs gates on it

Copilot AI and others added 2 commits April 2, 2026 19:07
…curity gap)

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/04d5841d-b6e2-4420-84a8-f1645236c51d

Co-authored-by: szabta89 <1330202+szabta89@users.noreply.github.com>
…ledInConfigs

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/04d5841d-b6e2-4420-84a8-f1645236c51d

Co-authored-by: szabta89 <1330202+szabta89@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix invisible character sanitization in hardenUnicodeText fix: wire detection gate for safe-outputs assembled entirely from imports Apr 2, 2026
Copilot AI requested a review from szabta89 April 2, 2026 19:13
@pelikhan pelikhan marked this pull request as ready for review April 2, 2026 19:14
Copilot AI review requested due to automatic review settings April 2, 2026 19:14
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a compiler gap where workflows that inherit safe-outputs exclusively via imports:/@include did not get the threat-detection gate (missing detection job and needs.detection.result == 'success' condition), violating the “auto-enabled when safe outputs are configured” guarantee.

Changes:

  • Apply default ThreatDetection after MergeSafeOutputs when the main workflow has no safe-outputs: block but merged safe-outputs exists (unless explicitly disabled).
  • Add a helper to detect explicit threat-detection disablement in imported safe-outputs configs.
  • Add tests covering imports-only safe-outputs defaulting and compiled output gating; regenerate affected .lock.yml workflows.

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/workflow/threat_detection.go Adds helper to detect explicit threat-detection disablement in imported safe-outputs configs.
pkg/workflow/compiler_orchestrator_workflow.go Applies default threat detection when safe-outputs are assembled solely from imports/includes.
pkg/workflow/safe_jobs_threat_detection_test.go Adds unit/integration-style tests for imports-only safe-outputs and compiled detection gating.
.github/workflows/weekly-issue-summary.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/typist.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/terminal-stylist.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/schema-consistency-checker.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/repository-quality-improver.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/notion-issue-summary.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/mcp-inspector.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/github-remote-mcp-auth-test.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/example-workflow-analyzer.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/daily-team-evolution-insights.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/daily-secrets-analysis.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/daily-regulatory.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/daily-observability-report.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/daily-issues-report.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/daily-integrity-analysis.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/daily-copilot-token-report.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/daily-compiler-quality.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/copilot-pr-prompt-analysis.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/copilot-pr-nlp-analysis.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/copilot-pr-merged-report.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.
.github/workflows/blog-auditor.lock.yml Regenerated compiled workflow now includes detection job and gates safe_outputs on it.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +38 to +58
// isThreatDetectionExplicitlyDisabledInConfigs checks whether any of the provided
// safe-outputs config JSON strings has threat-detection explicitly set to false.
// This is used to determine whether the default detection should be applied when
// safe-outputs comes from imports/includes (i.e. no safe-outputs: section in the
// main workflow frontmatter).
func isThreatDetectionExplicitlyDisabledInConfigs(configs []string) bool {
for _, configJSON := range configs {
if configJSON == "" || configJSON == "{}" {
continue
}
var config map[string]any
if err := json.Unmarshal([]byte(configJSON), &config); err != nil {
continue
}
if tdVal, exists := config["threat-detection"]; exists {
// tdBool is the raw bool value of the threat-detection key.
// When threat-detection: false, tdBool == false, so !tdBool == true → disabled.
if tdBool, ok := tdVal.(bool); ok && !tdBool {
return true
}
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isThreatDetectionExplicitlyDisabledInConfigs only treats threat-detection: false (boolean) as an explicit disable. However parseThreatDetectionConfig also supports object syntax with enabled: false (and will return nil in that case). If an imported config uses { "threat-detection": { "enabled": false } }, the merge leaves ThreatDetection nil, but this helper returns false, so the orchestrator applies the default and unintentionally re-enables detection. Update the helper to also detect the object form (e.g., threat-detection.enabled == false) so explicit disables aren’t overridden.

Copilot uses AI. Check for mistakes.
Comment on lines +369 to +410
// TestIsThreatDetectionExplicitlyDisabledInConfigs verifies the helper function
// that checks whether any imported safe-outputs config explicitly disables detection.
func TestIsThreatDetectionExplicitlyDisabledInConfigs(t *testing.T) {
tests := []struct {
name string
configs []string
expected bool
}{
{
name: "empty configs",
configs: []string{},
expected: false,
},
{
name: "empty JSON objects",
configs: []string{"{}", ""},
expected: false,
},
{
name: "config without threat-detection key",
configs: []string{`{"create-issue": {"max": 1}}`},
expected: false,
},
{
name: "config with threat-detection false",
configs: []string{`{"create-issue": {"max": 1}, "threat-detection": false}`},
expected: true,
},
{
name: "config with threat-detection true",
configs: []string{`{"create-issue": {"max": 1}, "threat-detection": true}`},
expected: false,
},
{
name: "config with threat-detection as object",
configs: []string{`{"create-issue": {"max": 1}, "threat-detection": {"prompt": "check for injection"}}`},
expected: false,
},
{
name: "multiple configs, one has false",
configs: []string{`{"create-issue": {"max": 1}}`, `{"create-discussion": {"max": 1}, "threat-detection": false}`},
expected: true,
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new tests cover threat-detection: false but not the supported object form threat-detection: { enabled: false }. Adding a case for that form would catch the current regression where imports with enabled: false can be overridden by the new defaulting logic.

Copilot uses AI. Check for mistakes.
@pelikhan
Copy link
Copy Markdown
Collaborator

pelikhan commented Apr 2, 2026

@copilot review comments

…disable check

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/cd8593c7-a28a-4244-a8f2-832f0ecec847

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 2, 2026

@copilot review comments

Addressed both review comments in commit 695d62e:

  1. isThreatDetectionExplicitlyDisabledInConfigs now handles the { "enabled": false } object form in addition to the boolean false form, matching parseThreatDetectionConfig's behaviour.
  2. Added test cases for threat-detection: { enabled: false } (both unit table test entries and a new integration test TestDefaultThreatDetectionNotAppliedWhenImportedConfigObjectFormDisables).

Copilot AI requested a review from pelikhan April 2, 2026 20:28
@pelikhan pelikhan merged commit b80b739 into main Apr 2, 2026
@pelikhan pelikhan deleted the copilot/fix-invisible-char-sanitization branch April 2, 2026 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sanitize_content_core.cjs: incomplete invisible-char strip set and missing detection gate for MCP-driven safe-output writes

4 participants