Skip to content

Surface external threat-detection engine failures in warn-mode fallback#44605

Merged
pelikhan merged 4 commits into
mainfrom
copilot/deep-report-investigate-failed-results
Jul 10, 2026
Merged

Surface external threat-detection engine failures in warn-mode fallback#44605
pelikhan merged 4 commits into
mainfrom
copilot/deep-report-investigate-failed-results

Conversation

Copilot AI commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Warn-mode threat detection could fall back to a generic agent_failure when the external detector exited without producing detection_result.json, leaving the real failure mode buried in logs. In the reported run, the detector emitted THREAT_DETECTION_STATUS: reason=engine_error exit=2, but the conclude step only surfaced “failed to produce results.”

  • What changed

    • Updated the external detector conclude wrapper to inspect the sibling detection.log when detection_result.json is missing.
    • If present, the wrapper now includes the last THREAT_DETECTION_STATUS: line in the warning/error message.
    • If the log exists but has no status line, or the log is missing entirely, the fallback message now says so explicitly instead of silently collapsing to a generic missing-file warning.
  • Why this matters

    • Keeps warn-mode behavior non-blocking while making “no verdict produced” failures diagnosable from the surfaced output.
    • Preserves the existing detection gate contract (conclusion=warning, reason=agent_failure) while making the alert materially more actionable.
  • Coverage

    • Added focused coverage for the missing-result path to verify detector status lines are surfaced and the warning-path outputs remain unchanged.
if [ "${RUN_DETECTION:-false}" = "true" ] && [ ! -f "${RESULT_FILE}" ]; then
  detection_status="$(grep "${DETECTION_STATUS_PREFIX}" "${DETECTION_LOG_FILE}" | tail -n 1 || true)"
  result_message="Detection result file not found at: ${RESULT_FILE} (execution outcome: ${DETECTION_AGENTIC_EXECUTION_OUTCOME:-unknown})"
  result_message="${result_message}; detector status: ${detection_status}"
fi

Copilot AI and others added 3 commits July 9, 2026 18:33
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Investigate threat-detection engine failed to produce results Surface external threat-detection engine failures in warn-mode fallback Jul 9, 2026
Copilot AI requested a review from pelikhan July 9, 2026 18:41
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

PR Triage - Run 29041860272. Category: bug | Risk: Low | Score: 48/100 | Action: defer. DRAFT. Small fix (68+/2-) surfacing threat-detection engine error details in warn-mode. Undraft and let CI pass to re-evaluate for auto_merge.

Generated by 🔧 PR Triage Agent · 78.2 AIC · ⌖ 8.53 AIC · ⊞ 5.4K ·

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage — Run §29061171191

Field Value
Category bug
Risk 🟢 Low
Score 38/100 (Impact 18 + Urgency 10 + Quality 10)
Action 🔃 defer

Surface external threat-detection engine failures in warn-mode fallback. DRAFT, 2 files, 68 additions. Low risk. Defer until undrafted.

Generated by 🔧 PR Triage Agent · 155.8 AIC · ⌖ 8.06 AIC · ⊞ 5.4K ·

@pelikhan pelikhan marked this pull request as ready for review July 10, 2026 04:03
Copilot AI review requested due to automatic review settings July 10, 2026 04:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request improves diagnosability of warn-mode threat detection failures by enhancing the conclude_threat_detection.sh wrapper to surface the detector’s last THREAT_DETECTION_STATUS: line when detection_result.json is missing, and by adding test coverage for this missing-result behavior.

Changes:

  • Updated the conclude wrapper to look for a sibling detection.log and include the last THREAT_DETECTION_STATUS: line in the surfaced warning/error message when results are missing.
  • Improved fallback messaging to distinguish between “log missing” vs “log present but no status line”.
  • Added a focused Go test verifying detector status lines and execution outcome are surfaced while preserving warn-mode contract outputs.
Show a summary per file
File Description
actions/setup/sh/conclude_threat_detection.sh Enhances missing-result fallback messaging by incorporating detector status from detection.log.
pkg/workflow/threat_detection_conclude_script_test.go Adds test coverage ensuring warn-mode output surfaces detector status and preserves conclusion/reason outputs.

Review details

Tip

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

  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Low


if [ "${continue_on_error}" = "true" ]; then
echo "::warning::Detection result file not found at: ${RESULT_FILE} (execution outcome: ${DETECTION_AGENTIC_EXECUTION_OUTCOME:-unknown}); continuing because GH_AW_DETECTION_CONTINUE_ON_ERROR=true"
echo "::warning::${result_message}; continuing because GH_AW_DETECTION_CONTINUE_ON_ERROR=true"
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #44605 does not have the 'implementation' label and has only 49 new lines of code in business logic directories (threshold: 100).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: Surface external threat-detection engine failures in warn-mode fallback

The change is focused and correct. The shell logic is well-structured and the new test exercises the primary code path.

One non-blocking observation: the new branches for "log exists but has no status line" and "log file not found" are untested. While the existing test matrix gives reasonable confidence, adding coverage for those two branches would make future regressions immediately visible.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 10.7 AIC · ⌖ 4.29 AIC · ⊞ 4.8K

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 80/100 — Excellent

Analyzed 3 test(s): 3 design, 0 implementation, 0 violation(s).

📊 Metrics (3 tests)
Metric Value
Analyzed 3 (Go: 3, JS: 0)
✅ Design 3 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 2 (67%)
Duplicate clusters 0
Inflation YES (test 49 lines / prod 19 lines = 2.57:1)
🚨 Violations 0
Test File Classification Issues
TestConcludeThreatDetectionScript_MissingResultContinueOnError threat_detection_conclude_script_test.go:13 design_test / high_value None
TestConcludeThreatDetectionScript_MissingResultSurfacesDetectorStatus threat_detection_conclude_script_test.go:64 design_test / high_value None
TestConcludeThreatDetectionScript_InvokesThreatDetectConclude threat_detection_conclude_script_test.go:111 design_test / high_value Happy-path only
⚠️ Flagged Tests (1)

TestConcludeThreatDetectionScript_InvokesThreatDetectConclude (threat_detection_conclude_script_test.go:111) — happy-path only, no error/failure branch. The test verifies the CLI stub is invoked correctly on success but does not cover what happens when threat-detect conclude exits non-zero. Consider adding a companion test for engine failure surfacing.

Verdict

Passed. 0% implementation tests (threshold: 30%). Score 80/100. Minor: test inflation ratio 2.57:1 (shell script integration tests are necessarily more verbose — acceptable). One test lacks error-path coverage.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 29 AIC · ⌖ 9.55 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 80/100. 0% implementation tests (threshold: 30%). All 3 tests verify behavioral contracts via GITHUB_OUTPUT/GITHUB_ENV assertions. Minor: one test lacks error-path coverage.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /tdd and /diagnosing-bugs — requesting changes on two issues.

📋 Key Themes & Highlights

Key Themes

  • Missing branch coverage: only the happy-path branch (log exists + status line found) is tested; the two other new branches (log exists but no status, log missing) have no coverage.
  • grep without -F: the prefix match could behave unexpectedly if the prefix ever contains regex metacharacters.

Positive Highlights

  • ✅ Three-tier fallback is well-structured and covers all reachable log states.
  • ✅ Existing test extended cleanly; detectorStatusLine constant promotes reuse.
  • ✅ Preserves the existing gate contract (conclusion=warning, reason=agent_failure) — non-blocking behaviour is unchanged.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 27.9 AIC · ⌖ 4.55 AIC · ⊞ 6.6K
Comment /matt to run again


func TestConcludeThreatDetectionScript_InvokesThreatDetectConclude(t *testing.T) {
tmpDir := t.TempDir()
scriptPath := filepath.Join("..", "..", "actions", "setup", "sh", "conclude_threat_detection.sh")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/tdd] Only one of three new branches is tested — the "log exists but has no status line" and "log not found" paths are uncovered.

💡 Suggested additions
func TestConcludeThreatDetectionScript_MissingResultLogExistsNoStatus(t *testing.T) {
    // Write a log with no THREAT_DETECTION_STATUS line
    os.WriteFile(detectionLog, []byte("some other output\n"), 0600)
    // ... run script, assert message contains "did not include THREAT_DETECTION_STATUS:"
}

func TestConcludeThreatDetectionScript_MissingResultNoLog(t *testing.T) {
    // Do not write detection.log
    // ... run script, assert message contains "detection log not found at"
}

Without these, a regression in either branch (e.g., wrong variable reference) would go undetected.

@copilot please address this.

DETECTION_STATUS_PREFIX="THREAT_DETECTION_STATUS:"
continue_on_error="${GH_AW_DETECTION_CONTINUE_ON_ERROR:-true}"
continue_on_error="$(echo "${continue_on_error}" | tr '[:upper:]' '[:lower:]')"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/diagnosing-bugs] grep without -F treats THREAT_DETECTION_STATUS: as a regex pattern — : and any future special characters in the prefix could cause unexpected matches or failures.

💡 Fix
detection_status="$(grep -F "${DETECTION_STATUS_PREFIX}" "${DETECTION_LOG_FILE}" | tail -n 1 || true)"

Use -F (fixed-string) to match literally and avoid unintended regex interpretation.

@copilot please address this.

@github-actions github-actions Bot mentioned this pull request Jul 10, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

REQUEST_CHANGES — security-adjacent issues must be fixed before merge

The diagnostic improvement is worthwhile, but the PR introduces a trust-boundary violation by embedding log file content directly into GitHub Actions workflow annotations and output files without sanitization.

Blocking themes
  1. Workflow command injection (critical)detection_status from the external detector's log is interpolated verbatim into ::warning::.... A log line containing %0A::set-env:: or raw LF will cause the runner to process injected workflow commands. Both %-encoding and newline stripping are required before any echo ::...:: emission.

  2. GITHUB_OUTPUT corruption (high) — Multiline or newline-embedded content in detection_status flows into result_message, which can break the key=value format expected by GITHUB_OUTPUT, allowing attacker-controlled lines to override downstream step outputs. tr -d '\r\n' on the grep result is required.

  3. grep without -F (medium)DETECTION_STATUS_PREFIX is used as a regex, not a fixed string. Use grep -F to prevent future metacharacter accidents.

Test coverage for the continue_on_error=false (error exit) path with the new status message is also missing but is not individually blocking.

🔎 Code quality review by PR Code Quality Reviewer · 32.4 AIC · ⌖ 4.76 AIC · ⊞ 5.4K
Comment /review to run again

echo "::warning::${result_message}; continuing because GH_AW_DETECTION_CONTINUE_ON_ERROR=true"
echo "conclusion=warning" >> "${GITHUB_OUTPUT}"
echo "success=false" >> "${GITHUB_OUTPUT}"
echo "reason=agent_failure" >> "${GITHUB_OUTPUT}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Workflow command injection: detection_status from an external log file flows unsanitized into a ::warning:: annotation and can inject arbitrary workflow commands.

💡 Details and fix

A detector log line like:

THREAT_DETECTION_STATUS: foo%0A::add-mask::mysecret

will cause the runner to process ::add-mask::mysecret as a real workflow command — masking or leaking arbitrary values. On older runners ::set-env:: could corrupt runner environment state.

The existing reviewer comment addresses % encoding but raw LF/CR bytes in the log file have the same effect. Sanitize before embedding:

detection_status="$(printf '%s' "${detection_status}" | tr -d '\r\n' | sed 's/%/%25/g')"
result_message="$(printf '%s' "${result_message}" | tr -d '\r\n' | sed 's/%/%25/g')"

Apply this to every value derived from log file content before it reaches any echo ::...:: or >> "${GITHUB_OUTPUT}" write.

if [ -f "${DETECTION_LOG_FILE}" ]; then
detection_status="$(grep "${DETECTION_STATUS_PREFIX}" "${DETECTION_LOG_FILE}" | tail -n 1 || true)"
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Multiline detection_status corrupts GITHUB_OUTPUT: if the log file contains multiple THREAT_DETECTION_STATUS: lines (or a line with embedded newlines), the grep+tail result can still be multi-line, breaking the key=value format written to GITHUB_OUTPUT.

💡 Details and fix

tail -n 1 only helps if grep returns clean single lines. A log line with an embedded literal \n (e.g., from ANSI sequences or log formatting) survives through grep | tail. The result is then interpolated into:

echo "conclusion=warning" >> "${GITHUB_OUTPUT}"

...but result_message containing a newline will inject an attacker-controlled extra line into GITHUB_OUTPUT, potentially overriding downstream step outputs.

Fix: collapse detection_status to a single line before use:

detection_status="$(grep -F "${DETECTION_STATUS_PREFIX}" "${DETECTION_LOG_FILE}" | tail -n 1 | tr -d '\r\n')"

Note the addition of -F (fixed string) to prevent the prefix from being treated as a regex, and tr -d '\r\n' to strip any embedded line endings.

if [ -f "${DETECTION_LOG_FILE}" ]; then
detection_status="$(grep "${DETECTION_STATUS_PREFIX}" "${DETECTION_LOG_FILE}" | tail -n 1 || true)"
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

grep without -F flag is a latent correctness bug: grep "${DETECTION_STATUS_PREFIX}" treats the prefix as a regex pattern. While the current literal THREAT_DETECTION_STATUS: is harmless, the variable is not readonly and a future change or env override introducing regex metacharacters (e.g., ., *, [) would silently broaden the match and capture unintended log lines into the warning.

💡 Fix
detection_status="$(grep -F "${DETECTION_STATUS_PREFIX}" "${DETECTION_LOG_FILE}" | tail -n 1 || true)"

Using -F (fixed-string mode) makes the match deterministic regardless of the prefix value.

@pelikhan pelikhan merged commit da78350 into main Jul 10, 2026
89 checks passed
@pelikhan pelikhan deleted the copilot/deep-report-investigate-failed-results branch July 10, 2026 04:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[deep-report] Investigate threat-detection engine "failed to produce results" (warn-mode flag)

3 participants