Repository navigation
Allow opting out of "[aw] Detection Runs" tracking issue independently of threat detection #61428
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
ab72688
4f83aca
606c99c
c56c00c
7088269
03afa9f
8e646ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11911,6 +11911,11 @@ | |
| "continue-on-error": { | ||
| "$ref": "#/$defs/templatable_boolean", | ||
| "description": "When true (default), detection failures produce warnings and allow safe outputs to proceed with a caution notice and 'needs-review' label. When false, detection failures block safe outputs entirely. Accepts a boolean literal or a GitHub Actions expression." | ||
| }, | ||
| "report-as-issue": { | ||
| "type": "boolean", | ||
| "default": true, | ||
| "description": "When true (default), detection warnings/failures create or update the '[aw] Detection Runs' tracking issue and post a comment to it. When false, detection still runs and enforces its configured continue-on-error behavior, but no tracking issue is created or updated; results remain available in the GitHub Actions run diagnostics." | ||
|
Comment on lines
+11915
to
+11918
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regenerated |
||
| } | ||
| }, | ||
| "additionalProperties": false | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ type ThreatDetectionConfig struct { | |
| RunsOn string `yaml:"runs-on,omitempty"` // Runner override for the detection job | ||
| Environment string `yaml:"environment,omitempty"` // GitHub Actions environment override for the detection job (defaults to top-level environment when OIDC is used) | ||
| ContinueOnError *bool `yaml:"continue-on-error,omitempty"` // When true (default), detection failures produce warnings instead of blocking safe outputs | ||
| ReportAsIssue *bool `yaml:"report-as-issue,omitempty"` // When true (default), detection warnings/failures create/update the "[aw] Detection Runs" tracking issue | ||
| EnabledExpr *string `yaml:"-"` // Expression form of the enabled flag, e.g. "${{ inputs.enable-threat-detection }}" | ||
| ContinueOnErrorExpr *string `yaml:"-"` // Expression form of continue-on-error, e.g. "${{ inputs.coe }}" | ||
| } | ||
|
|
@@ -33,6 +34,13 @@ func (td *ThreatDetectionConfig) IsContinueOnError() bool { | |
| return td.ContinueOnError == nil || *td.ContinueOnError | ||
| } | ||
|
|
||
| // IsReportAsIssueEnabled reports whether detection warnings/failures should create | ||
| // or update the "[aw] Detection Runs" tracking issue. Defaults to true (enabled) | ||
| // when not explicitly set, preserving existing behaviour. | ||
| func (td *ThreatDetectionConfig) IsReportAsIssueEnabled() bool { | ||
| return td == nil || td.ReportAsIssue == nil || *td.ReportAsIssue | ||
| } | ||
|
|
||
| // HasRunnableDetection reports whether this config will produce a detection job | ||
| // that actually executes. Returns false when the engine is disabled and no | ||
| // custom steps are configured, since the job would have nothing to run. | ||
|
|
@@ -118,6 +126,17 @@ func (c *Compiler) parseThreatDetectionConfig(outputMap map[string]any) *ThreatD | |
| func (c *Compiler) parseThreatDetectionObjectConfig(configMap map[string]any) *ThreatDetectionConfig { | ||
| threatConfig := &ThreatDetectionConfig{} | ||
|
|
||
| parseThreatDetectionScalarFields(configMap, threatConfig) | ||
| c.parseThreatDetectionEngineField(configMap, threatConfig) | ||
|
|
||
| threatLog.Printf("Threat detection configured with custom prompt: %v, custom pre-steps: %v, custom post-steps: %v", threatConfig.Prompt != "", len(threatConfig.Steps) > 0, len(threatConfig.PostSteps) > 0) | ||
| return threatConfig | ||
| } | ||
|
|
||
| // parseThreatDetectionScalarFields parses the non-engine fields of the threat-detection | ||
| // object configuration (prompt, steps, post-steps, budgets, runner, and reporting flags) | ||
| // into threatConfig. | ||
| func parseThreatDetectionScalarFields(configMap map[string]any, threatConfig *ThreatDetectionConfig) { | ||
| // Parse prompt field | ||
| if prompt, exists := configMap["prompt"]; exists { | ||
| if promptStr, ok := prompt.(string); ok { | ||
|
|
@@ -170,6 +189,13 @@ func (c *Compiler) parseThreatDetectionObjectConfig(configMap map[string]any) *T | |
| threatConfig.RunsOn = renderRunsOnSnippet(runOn) | ||
| } | ||
|
|
||
| parseThreatDetectionReportingFields(configMap, threatConfig) | ||
| } | ||
|
|
||
| // parseThreatDetectionReportingFields parses the fields that control how detection | ||
| // results are enforced and reported (continue-on-error and report-as-issue) into | ||
| // threatConfig. | ||
| func parseThreatDetectionReportingFields(configMap map[string]any, threatConfig *ThreatDetectionConfig) { | ||
| // Parse continue-on-error field (default: true). | ||
| // Accepts a literal bool or a GitHub Actions expression string. | ||
| if coe, exists := configMap["continue-on-error"]; exists { | ||
|
|
@@ -185,31 +211,41 @@ func (c *Compiler) parseThreatDetectionObjectConfig(configMap map[string]any) *T | |
| } | ||
| } | ||
|
|
||
| // Parse engine field (supports string, object, and boolean false formats) | ||
| if engine, exists := configMap["engine"]; exists { | ||
| // Handle boolean false to disable AI engine | ||
| if engineBool, ok := engine.(bool); ok { | ||
| if !engineBool { | ||
| threatLog.Print("Threat detection AI engine disabled") | ||
| // engine: false means no AI engine steps | ||
| threatConfig.EngineConfig = nil | ||
| threatConfig.EngineDisabled = true | ||
| } | ||
| } else if engineStr, ok := engine.(string); ok { | ||
| threatLog.Printf("Threat detection engine set to: %s", engineStr) | ||
| // Handle string format | ||
| threatConfig.EngineConfig = &EngineConfig{ID: engineStr} | ||
| } else if engineObj, ok := engine.(map[string]any); ok { | ||
| threatLog.Print("Parsing threat detection engine configuration") | ||
| // Handle object format - use extractEngineConfig logic | ||
| _, engineConfig, model := c.ExtractEngineConfig(map[string]any{"engine": engineObj}) | ||
| threatConfig.EngineConfig = engineConfig | ||
| threatConfig.Model = model | ||
| // Parse report-as-issue field (default: true). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/tdd] 💡 Suggested addition to TestParseThreatDetectionConfig{
name: "object with report-as-issue false",
outputMap: map[string]any{
"threat-detection": map[string]any{
"report-as-issue": false,
},
},
expectedConfig: &ThreatDetectionConfig{
ReportAsIssue: boolPtr(false),
},
},This keeps parser-level coverage symmetric with @copilot please address this.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added parser-level coverage for |
||
| if reportAsIssue, exists := configMap["report-as-issue"]; exists { | ||
| if boolVal, ok := reportAsIssue.(bool); ok { | ||
| threatConfig.ReportAsIssue = &boolVal | ||
| threatLog.Printf("Threat detection report-as-issue set to: %v", boolVal) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| threatLog.Printf("Threat detection configured with custom prompt: %v, custom pre-steps: %v, custom post-steps: %v", threatConfig.Prompt != "", len(threatConfig.Steps) > 0, len(threatConfig.PostSteps) > 0) | ||
| return threatConfig | ||
| // parseThreatDetectionEngineField parses the engine field (supports string, object, and | ||
| // boolean false formats) into threatConfig. | ||
| func (c *Compiler) parseThreatDetectionEngineField(configMap map[string]any, threatConfig *ThreatDetectionConfig) { | ||
| engine, exists := configMap["engine"] | ||
| if !exists { | ||
| return | ||
| } | ||
| // Handle boolean false to disable AI engine | ||
| if engineBool, ok := engine.(bool); ok { | ||
| if !engineBool { | ||
| threatLog.Print("Threat detection AI engine disabled") | ||
| // engine: false means no AI engine steps | ||
| threatConfig.EngineConfig = nil | ||
| threatConfig.EngineDisabled = true | ||
| } | ||
| } else if engineStr, ok := engine.(string); ok { | ||
| threatLog.Printf("Threat detection engine set to: %s", engineStr) | ||
| // Handle string format | ||
| threatConfig.EngineConfig = &EngineConfig{ID: engineStr} | ||
| } else if engineObj, ok := engine.(map[string]any); ok { | ||
| threatLog.Print("Parsing threat detection engine configuration") | ||
| // Handle object format - use extractEngineConfig logic | ||
| _, engineConfig, model := c.ExtractEngineConfig(map[string]any{"engine": engineObj}) | ||
| threatConfig.EngineConfig = engineConfig | ||
| threatConfig.Model = model | ||
| } | ||
| } | ||
|
|
||
| // extractRawExpression strips the "${{" prefix and "}}" suffix from a GitHub Actions | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.