Skip to content

Content scope dependabot - #2727

Merged
jonathanKingston merged 25 commits into
mainfrom
jkt/auto/content-scope-dependabot-591f
Jun 8, 2026
Merged

Content scope dependabot#2727
jonathanKingston merged 25 commits into
mainfrom
jkt/auto/content-scope-dependabot-591f

Conversation

@jonathanKingston

@jonathanKingston jonathanKingston commented May 27, 2026

Copy link
Copy Markdown
Contributor

Asana Task/Github Issue: https://app.asana.com/1/137249556945/project/1201614831475344/task/1211495549578747?focus=true

Description

This change auto reviews Dependabot changes that have 14 days cooldown. It ensures that we pass all the other internal checks and then auto validates that with the model. It should be restricted only to Dependabot and has limited risk as upstreams will still manage their merges.

Testing Steps

Checklist

Please tick all that apply:

  • I have tested this change locally
  • I have tested this change locally in all supported browsers
  • This change will be visible to users
  • I have added automated tests that cover this change
  • I have ensured the change is gated by config
  • This change was covered by a ship review
  • This change was covered by a tech design
  • Any dependent config has been merged

Note

High Risk
Automated PR approval and merge for dependency updates depends on external AI and Cursor checks; misconfiguration or model errors could merge unwanted npm changes, though multiple fail-closed guards limit spoofing and stale commits.

Overview
Adds an Anthropic-backed safety gate for automated Dependabot npm PR handling, and tightens Dependabot scheduling.

Dependabot config switches npm updates to daily scans with a 14-day cooldown (was weekly / 2-day cooldown).

Auto-merge workflow no longer blanket-enables merge or patch-only auto-approve (including the previous ignored-package list). For same-repo dependabot[bot] npm PRs, it sparse-checkouts dependabot-anthropic-gate.mjs from the base branch, waits for CI plus three trusted Cursor checks, gathers cursor[bot] evidence (reviews, issue comments, inline Bugbot comments), and asks Claude for a strict safe_to_merge JSON decision. Approval and --auto --merge run only when the model approves and the PR head SHA still matches the assessed commit.

The new gate script is fail-closed: verifies Cursor checks by app slug and cursor.com details URL, dedupes check runs by app+name, allowlists automation authors, requires actionable evidence, and rejects malformed or injectable model output.

CI adds a github-scripts-unit job (node --test on the gate helpers) and includes it in the CI gate.

Reviewed by Cursor Bugbot for commit 05bae11. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions github-actions Bot added the semver-patch Bug fix / internal — no release needed label May 27, 2026
@github-actions
github-actions Bot requested review from dbajpeyi, dharb and shakyShane May 27, 2026 23:21
@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

[Beta] Generated file diff

Time updated: Mon, 08 Jun 2026 19:34:33 GMT

@jonathanKingston

Copy link
Copy Markdown
Contributor Author

@shakyShane, @dbajpeyi or @dharb This is to add auto-merging of npm package changes that have happened after a 14-day cooldown period. We validate with Cursor and the various other automations that we have, and use Anthropic to validate all of these automations. If they all pass, then we merge automatically, assuming that all tests pass.

@jonathanKingston
jonathanKingston marked this pull request as ready for review May 27, 2026 23:26
@jonathanKingston
jonathanKingston requested a review from a team as a code owner May 27, 2026 23:26
Comment thread .github/workflows/dependabot-auto-merge.yml Outdated
Comment thread .github/scripts/dependabot-anthropic-gate.mjs
Comment thread .github/scripts/dependabot-anthropic-gate.mjs
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Cursor review was not successful.

This PR requires a manual review and approval from a member of one of the following teams:

  • @duckduckgo/content-scope-scripts-owners
  • @duckduckgo/apple-devs
  • @duckduckgo/android-devs
  • @duckduckgo/team-windows-development
  • @duckduckgo/extension-owners
  • @duckduckgo/config-aor
  • @duckduckgo/breakage-aor
  • @duckduckgo/breakage

Comment thread .github/scripts/dependabot-anthropic-gate.mjs Outdated
@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Build Branch

Branch pr-releases/jkt/auto/content-scope-dependabot-591f
Commit 507755ec31
Updated June 8, 2026 at 7:33:57 PM UTC

Static preview entry points

QR codes (mobile preview)
Entry point QR code
Docs QR for docs preview
Static pages QR for static pages preview
Integration pages QR for integration pages preview

Integration commands

npm (Android / Extension):

npm i github:duckduckgo/content-scope-scripts#pr-releases/jkt/auto/content-scope-dependabot-591f

Swift Package Manager (Apple):

.package(url: "https://github.com/duckduckgo/content-scope-scripts.git", branch: "pr-releases/jkt/auto/content-scope-dependabot-591f")

git submodule (Windows):

git -C submodules/content-scope-scripts fetch origin pr-releases/jkt/auto/content-scope-dependabot-591f
git -C submodules/content-scope-scripts checkout origin/pr-releases/jkt/auto/content-scope-dependabot-591f
Pin to exact commit

npm (Android / Extension):

npm i github:duckduckgo/content-scope-scripts#507755ec31cb868a61d05b0acb13b3703b127142

Swift Package Manager (Apple):

.package(url: "https://github.com/duckduckgo/content-scope-scripts.git", revision: "507755ec31cb868a61d05b0acb13b3703b127142")

git submodule (Windows):

git -C submodules/content-scope-scripts fetch origin pr-releases/jkt/auto/content-scope-dependabot-591f
git -C submodules/content-scope-scripts checkout 507755ec31cb868a61d05b0acb13b3703b127142

Comment thread .github/workflows/dependabot-auto-merge.yml
Comment thread .github/scripts/dependabot-anthropic-gate.mjs Outdated
Comment thread .github/scripts/dependabot-anthropic-gate.mjs Outdated

@cursor cursor 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.

Stale comment

Web Compatibility Assessment

No injected-runtime findings. The current diff is limited to .github/dependabot.yml, .github/workflows/dependabot-auto-merge.yml, and .github/scripts/dependabot-anthropic-gate.mjs; it does not touch injected/src, wrapper utilities, captured globals, messaging transports, platform entry points, package manifests, or lockfiles.

Security Assessment

  1. .github/scripts/dependabot-anthropic-gate.mjs lines 375-379, severity: error
    Existing open finding still applies: the Anthropic gate fetches top-level review bodies and issue comments, but not inline PR review comments. Cursor/Bugbot findings are often inline-only with empty review bodies, so the final gate can miss blocking automation feedback.

  2. .github/workflows/dependabot-auto-merge.yml lines 7-10, severity: warning
    Existing open finding still applies: fetchCurrentWorkflowCheckRunIds() calls the Actions Jobs API, but the explicit permissions block omits actions: read. That can 403 before the gate reaches its fail-closed decision.

  3. .github/workflows/dependabot-auto-merge.yml lines 43-71, severity: warning
    Existing open finding still applies: this privileged pull_request_target workflow passes GITHUB_TOKEN to fountainhead/action-wait-for-check@v1.2.0, a mutable third-party tag. Pin it to a full commit SHA or move the wait logic into checked-in base-branch code.

Previously reported issues for name-only Cursor check trust and untrusted review/comment authors are addressed in the latest commit via run.app.slug / details_url host validation and TRUSTED_AUTOMATION_AUTHORS filtering.

Risk Level

High Risk for workflow/security automation. Injected runtime risk is low, but this PR adds privileged auto-approval and auto-merge machinery for future dependency updates.

Recommendations

Fetch trusted inline review comments before asking Anthropic, add actions: read, and pin or replace the third-party wait action.

Validation: node --check .github/scripts/dependabot-anthropic-gate.mjs passes.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

Comment thread .github/scripts/dependabot-anthropic-gate.mjs Outdated
Comment thread .github/workflows/dependabot-auto-merge.yml
Comment thread .github/workflows/dependabot-auto-merge.yml
@cursor
cursor Bot force-pushed the jkt/auto/content-scope-dependabot-591f branch from 6e35b15 to 6121e20 Compare May 28, 2026 10:17
Comment thread .github/scripts/dependabot-anthropic-gate.mjs
Comment thread .github/scripts/dependabot-anthropic-gate.mjs
Comment thread .github/scripts/dependabot-anthropic-gate.mjs Outdated
@cursor
cursor Bot force-pushed the jkt/auto/content-scope-dependabot-591f branch from dab5c39 to 7e81412 Compare May 28, 2026 10:22

@cursor cursor 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.

Stale comment

Web Compatibility Assessment

No injected-runtime findings. The diff is limited to .github/dependabot.yml, .github/workflows/dependabot-auto-merge.yml, and .github/scripts/dependabot-anthropic-gate.mjs; it does not touch injected/src, wrapper utilities, captured globals, messaging transports, platform entry points, package manifests, or lockfiles.

Security Assessment

  1. .github/scripts/dependabot-anthropic-gate.mjs lines 467-470, severity: error
    The Anthropic gate still gathers top-level PR review bodies and issue comments, but not inline PR review comments / review threads. Cursor/Bugbot findings are often inline-only with empty review bodies, so the final merge gate can miss blocking automation feedback.

  2. .github/workflows/dependabot-auto-merge.yml lines 7-10, severity: warning
    The script calls GET /actions/runs/{run_id}/jobs via fetchCurrentWorkflowCheckRunIds(), but the explicit workflow permissions block grants checks: read and omits actions: read. With unspecified permissions set to none, this can 403 before the gate reaches its assessment.

Previously reported issues around Cursor check spoofing and untrusted review/comment author evidence are addressed in the current diff via run.app.slug / details_url host validation and TRUSTED_AUTOMATION_AUTHORS filtering.

Risk Level

High Risk for workflow/security automation. Injected runtime risk is low, but this PR adds privileged pull_request_target auto-approval/auto-merge machinery for future Dependabot npm updates.

Recommendations

  1. Fetch trusted inline review findings before asking Anthropic, preferably via GraphQL reviewThreads so resolved/outdated threads can be handled deliberately; /pulls/{pull_number}/comments is the simpler alternative if thread state is not needed.
  2. Add actions: read to the workflow permissions, or remove the Actions Jobs API dependency and self-exclude the gate another way.
  3. Add a mocked test/fixture where a Cursor run has an empty review body plus an inline blocking comment, and assert the gate evidence includes that finding.

Validation: git show 6121e20a8e159b7244989eff956251c9d8929f50:.github/scripts/dependabot-anthropic-gate.mjs | node --check --input-type=module - passes.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

Comment thread .github/workflows/dependabot-auto-merge.yml
Comment thread .github/workflows/dependabot-auto-merge.yml
Comment thread .github/scripts/dependabot-anthropic-gate.mjs

@cursor cursor 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.

Stale comment

Web Compatibility Assessment

No findings. The current diff is limited to .github/dependabot.yml, .github/workflows/dependabot-auto-merge.yml, and .github/scripts/dependabot-anthropic-gate.mjs; it does not touch injected/src, wrapper utilities, captured globals, messaging transports, platform entry points, package manifests, or lockfiles.

Security Assessment

No new findings in the current head. I rechecked the previously reported automation-gate concerns and they appear addressed: Cursor check runs are pinned by name + app slug + details URL host, review/comment evidence is filtered to trusted cursor[bot] authors, inline PR review comments are fetched, Bugbot evidence is matched by the trusted check head SHA, actions: read is granted for the Jobs API call, and the mutable third-party wait action is no longer used.

Risk Level

High Risk for workflow/security automation: injected runtime risk is low, but this adds privileged pull_request_target auto-approval/auto-merge machinery for future Dependabot npm updates.

Recommendations

Add fixture coverage for the gate script if practical, especially: spoofed check names from another app, untrusted comments containing agent IDs, inline-only Bugbot findings with details_url: https://cursor.com/docs/bugbot, and brace-balanced Anthropic JSON parsing.

Validation: node --check .github/scripts/dependabot-anthropic-gate.mjs passes.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

cursoragent and others added 18 commits June 5, 2026 10:28
The previous self-exclusion compared run.run_id (a field that does not
exist on the Check Runs API response) and then fell through to a
hardcoded run.name === 'dependabot' filter. That created a fragile
coupling to the YAML job key: any rename would silently include the
gate's own check run in waitForOtherChecksToPass and deadlock until
the 30-minute timeout.

Resolve the current workflow run's job IDs via the Actions Jobs API
(GitHub Actions sets job.id == check_run.id) and exclude any matching
check run from the wait set.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
matchedCursorSources() previously matched any review or issue comment
whose body contained the public Cursor agent id. Because the agent id
appears in plain text in PR comments, anyone with comment access could
echo it (or a fabricated transcript around it) and have the Anthropic
gate treat that text as authenticated Cursor-authored evidence —
prompt injection against the auto-merge decision.

Filter sources at construction time: drop any review/comment whose
author is not a GitHub App bot in the explicit
TRUSTED_AUTOMATION_AUTHORS allow-list (currently just 'cursor[bot]').
Untrusted authors are returned as null and stripped before sources
reach matchedCursorSources(). Also tighten the Anthropic system prompt
to reflect that filtering happens upstream.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
latestCheckRunsByName() previously matched expected Cursor checks
solely by display name. Any installed GitHub App with checks:write
permission could have published a later success with the same display
name, and the Anthropic gate would have treated it as authenticated
Cursor evidence.

Convert EXPECTED_CHECKS to structured entries that pin each check to:
 - run.name              (display name)
 - run.app.slug          (globally unique GitHub App slug 'cursor')
 - URL(run.details_url).host  ('cursor.com')

A new matchExpectedCheck() returns the matching expected entry only
when all three signals line up; latestCheckRunsByName() ignores
anything else. Updated main()'s missingChecks computation to read
expected.name from the new shape.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
Removed the three fountainhead/action-wait-for-check@v1.2.0 steps and
the Validate Cursor checks step from dependabot-auto-merge.yml. That
action was referenced by mutable tag and was being handed GITHUB_TOKEN
on a pull_request_target job with contents:write / pull-requests:write,
which would let a tag retag or repo compromise of fountainhead use the
write-scoped token during Dependabot PR processing.

Folded the wait inline into dependabot-anthropic-gate.mjs, which is
already checked out from the trusted base branch via sparse-checkout
and runs with the same permissions:

  - Renamed waitForOtherChecksToPass -> waitForChecksToSettle and made
    it block until (a) every non-current check run on the head SHA is
    completed and passing, (b) commit statuses are non-pending and
    non-failed, AND (c) every EXPECTED_CHECKS entry has appeared as a
    matching trusted check run and reached 'completed' state. Without
    (c) the loop could exit before Cursor's checks were even
    registered, then fail with 'Missing expected Cursor checks'.
  - Renamed OTHER_CHECK_TIMEOUT_MS / OTHER_CHECK_POLL_INTERVAL_MS to
    CHECK_WAIT_TIMEOUT_MS / CHECK_WAIT_POLL_INTERVAL_MS to match.
  - Added missingExpectedCheckNames() and pendingExpectedCheckRuns()
    helpers reused for both the wait and the timeout error messages.

The Anthropic step now runs whenever the npm classification matches;
the script itself enforces all the previous gating, so failure modes
are unchanged from a caller's perspective.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
parseAnthropicDecision used /\{[\s\S]*\}/, which matches greedily
from the first '{' to the *last* '}' in the response. Any preamble
containing curly braces — e.g. 'Based on {evidence}, here is the JSON:
{...}' — would make the match span non-JSON content, JSON.parse would
throw, and a legitimate safe_to_merge=true decision would be lost.

Replace it with a generator candidateJsonObjects() that walks the text
emitting every brace-balanced '{...}' substring, tracking JSON string
literals and escapes so braces inside quoted values do not disturb
depth. parseAnthropicDecision now iterates candidates and returns the
first one that both parses as JSON and has the expected
{safe_to_merge:boolean, reason:string} shape, surfacing the last
parse/shape error if no candidate matches.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
The gate previously assembled evidence from two sources only:
  - GET /pulls/{pr}/reviews   (top-level review bodies)
  - GET /issues/{pr}/comments (issue / conversation comments)

It did not pull inline review comments (those attached to a diff
hunk) from GET /pulls/{pr}/comments. Cursor Bugbot publishes its
blocking findings as inline review comments with an empty parent
review body, so omitting that feed would let the Anthropic gate
auto-approve while concrete blocking findings sit unread on the PR.

Add a sourceFromInlineReviewComment builder (typed as
'inline_review_comment', carrying path/line/in_reply_to_id for audit)
gated by the existing isTrustedAutomationActor allow-list, and
include the new feed in main()'s Promise.all + sources merge. Trust
boundary is unchanged: untrusted authors still drop to null and are
filtered out before evidence is sent to Anthropic.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
fetchCurrentWorkflowCheckRunIds() calls GET /actions/runs/{run_id}/jobs
to map the current workflow run's jobs to their corresponding check-run
IDs, which is what lets waitForChecksToSettle() ignore its own check
run when deciding whether 'other' checks are pending.

Once a workflow declares an explicit permissions block, every scope
not listed defaults to 'none'. The block listed checks/contents/
pull-requests but omitted actions, so GITHUB_TOKEN would 403 against
the Actions Jobs API and the gate would error out before reaching its
fail-closed Anthropic decision.

Add 'actions: read' with an explanatory comment so the dependency is
documented at the permission site.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
matchedCursorSources() only matched bodies containing the agent id
extracted from a check run's details_url (/agents/<id>). That works
for the two 'Cursor Automation: ...' runs, but Cursor Bugbot uses a
generic https://cursor.com/docs/bugbot URL with no agent id, so
cursorAgentId() returned null and matchedCursorSources() returned []
for every Bugbot check — even after the inline-comment fetch landed.
The Anthropic gate could therefore auto-approve without ever seeing
the Bugbot inline findings on the PR.

Add a small sourceMatchesCheckRun() helper. Default match is still
the agent id; when the run is 'Cursor Bugbot' specifically, fall
back to matching trusted-author bodies that mention the check run's
head_sha. Bugbot's review summary, per-finding inline comments, and
threaded follow-ups all include a 'Reviewed by Cursor Bugbot for
commit <head_sha>' footer, so head_sha presence is a reliable
attribution signal that:

  - never re-trusts arbitrary comment authors (isTrustedAutomationActor
    has already filtered sources to cursor[bot] only),
  - scopes findings to the current revision (Bugbot comments for
    earlier commits with a different head_sha don't match),
  - does not depend on Bugbot's URL scheme or the 'BUGBOT_BUG_ID' /
    'BUGBOT_REVIEW' marker shape, so it survives Bugbot UI tweaks.

Verified with 5 cases: Bugbot inline finding for this head_sha
matches; Bugbot review summary for this head_sha matches; Bugbot
comment referencing a different head_sha is rejected; Cursor
Automation runs still match by agent id; Cursor Automation does not
fall back to head_sha.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
The last Cursor automation review reported no remaining security
findings but recommended fixture coverage for the gate script,
particularly: spoofed check names from another GitHub App, untrusted
comments containing agent ids, inline-only Bugbot findings with
details_url=https://cursor.com/docs/bugbot, and brace-balanced
Anthropic JSON parsing.

Make the script importable so it can be unit-tested without invoking
main():

  - Export the per-helper functions and the configuration constants
    (EXPECTED_CHECKS, TRUSTED_AUTOMATION_AUTHORS,
    PASSING_CHECK_CONCLUSIONS).
  - Guard the previous unconditional 'await main()' behind a
    fileURLToPath(import.meta.url) === process.argv[1] check so the
    workflow invocation still runs the gate but tests can import
    helpers safely.

Add dependabot-anthropic-gate.test.mjs following the existing
.github/scripts/review-helpers.test.mjs node:test / node:assert
convention, with 41 assertions across 16 suites covering:

  - matchExpectedCheck: trusted check accepted; spoofed app slug,
    wrong host, and unknown display name rejected.
  - latestCheckRunsByName: picks the latest matching run per name,
    rejects spoofed apps.
  - latestOtherCheckRunsByName / checkRunState: excludes own
    workflow's check-run ids; pending/failed/passing classification.
  - commitStatusState: pending vs failed.
  - missingExpectedCheckNames / pendingExpectedCheckRuns: report
    which expected Cursor checks have not yet appeared or are still
    in flight.
  - isTrustedAutomationActor and the three source builders:
    untrusted authors map to null, trusted authors produce the
    expected shape (path/line/inReplyToId on inline comments).
  - sourceMatchesCheckRun: Cursor Automation runs match by agent id;
    Cursor Bugbot runs match by head_sha; cross-commit Bugbot
    findings rejected; non-Bugbot runs do not fall back to head_sha.
  - matchedCursorSources: only surfaces the expected source per run.
  - evidenceForRun: long output text is truncated.
  - candidateJsonObjects / parseAnthropicDecision: preamble braces,
    multiple top-level objects, escape sequences, garbage input.
  - truncate, parseLinkHeader: utility behavior.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
Two unresolved review threads on the PR were still valid:

1. parseAnthropicDecision accepted the first valid-shape JSON object
   anywhere in the model response. Because evidence sent to Anthropic
   includes untrusted review/comment bodies, the model could be
   induced to quote {"safe_to_merge":true,...} from an attacker's
   comment before its real answer, and the parser would treat the
   quoted snippet as the merge decision.

   Replace candidateJsonObjects / first-match parsing with strict
   validation: the trimmed response must be exactly a single JSON
   object whose keys are exactly {safe_to_merge, reason, confidence},
   with type and enum validation on each field. Any preamble, code
   fence, trailing prose, extra key, or wrong type fails closed.
   candidateJsonObjects is no longer needed and is removed.

2. pendingExpectedCheckRuns previously returned every matching
   pending run without deduplicating by name, while the rest of the
   wait loop reasons about the latest run per name. A stale
   in_progress Cursor check sitting alongside a newer completed one
   for the same name would keep pendingCursor non-empty even when
   otherIdle was true, deadlocking waitForChecksToSettle() until the
   30-minute timeout fired.

   Route through latestCheckRunsByName() so only the latest matching
   run per name is considered, then filter for non-completed runs.

Test coverage updated: 11 stricter parseAnthropicDecision cases
(bare object, whitespace, preamble, trailing prose, code fence,
embedded snippet, extra keys, missing/invalid required fields,
invalid confidence enum, non-object JSON, empty input, garbage) and
a new stale-run dedup case for pendingExpectedCheckRuns. 46/46 pass
on node --test.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
The repo's tsconfig.json includes .github/scripts with checkJs:true and
strictNullChecks:true, so the gate test file participates in 'npm run
tsc'. Three categories of errors:

  - matchExpectedCheck() returns the entry or null; the
    EXPECTED_CHECKS.includes(matched) assertion was passing
    {entry}|null where {entry} was expected. Add an assert.ok(matched)
    null guard before the includes() call.
  - The local externalRun() helper inferred its conclusion parameter
    type from its default of null, so callers passing 'success' /
    'skipped' / 'failure' tripped TS2345. Annotate the helper with
    JSDoc so conclusion is typed as string | null.
  - sourceFromInlineReviewComment() returns the source object or
    null. The captures-path/line/in_reply_to_id test asserted on
    properties of the result without first narrowing it. Add
    assert.ok(src) before the property accesses.

No behavior change. tsc still reports three pre-existing errors in
injected/src/features/* about generated build files
(build/locales/*-locales.js, surrogates-generated.js); those are
unrelated to this PR and only appear when 'npm run build' has not
been run.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
…can't mask failures

latestOtherCheckRunsByName() previously deduped non-Cursor check runs
by display name only, so an installed GitHub App with checks:write
could publish a newer 'lint: success' check run and have it supersede
a failing 'lint' check run from github-actions before checkRunState()
saw the failure. With the gate auto-asking Anthropic on success, that
would silently drop a real CI failure from the gate's evidence.

Add a checkRunIdentityKey() that combines run.app.slug (falling back
to run.app.id, then null) with run.name, and dedupe on that composite
key. Each app's runs for a given name are tracked independently, so a
failure from any app still surfaces while reruns within the same app
still collapse to the latest run.

Tests:
  - new regression: github-actions reports 'lint: failure', another
    app posts a newer 'lint: success'; the failure must still appear
    in checkRunState().failed.
  - new regression: two reruns of 'lint' from the same app collapse
    to the latest run (status preserved).
  - existing tests updated to attach an explicit app.slug to the
    fixture so the new key works (default 'github-actions').

48/48 node --test cases pass; node --check, prettier, and eslint
clean. Pre-existing tsc errors in injected/src/features/* about
generated build files are unrelated and unchanged.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
The Anthropic gate can wait up to 30 minutes while evaluating a specific
head SHA. Re-read the current PR head before approving or enabling
auto-merge, bind approvals to commit_id, and pass --match-head-commit so
an older workflow run cannot approve or queue merge for a newer commit.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
@cursor
cursor Bot force-pushed the jkt/auto/content-scope-dependabot-591f branch from fced79c to 89d09d5 Compare June 5, 2026 10:29

@cursor cursor 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.

Stale comment

Injected PR Evaluation: Web Compatibility & Security

Scope: This synchronize diff touches only .github/dependabot.yml, .github/workflows/dependabot-auto-merge.yml, and .github/scripts/dependabot-anthropic-gate.{mjs,test.mjs}. There are no changes under injected/, messaging/, entry points, wrapper-utils.js, captured-globals.js, or feature code. Runtime web-compat / page-world security criteria below are not applicable to the diff itself; findings focus on supply-chain / CI merge safety and indirect risk to shipped JS.


Web Compatibility Assessment

File Lines Severity Finding
(none) info No injected API overrides, DOM hooks, messaging, or remote-config changes. Nothing in this diff can directly alter page-world behavior.
.github/dependabot.yml 6–8 info Schedule moves to daily with a 14-day cooldown. Operational only; does not change runtime. Increases how often dependency updates may land once auto-merge is enabled—breakage risk is indirect (bad npm bump merged), not an API-fidelity regression.

Security Assessment

File Lines Severity Finding
.github/workflows/dependabot-auto-merge.yml 4–16, 19–21 info Uses pull_request_target with contents: write / pull-requests: write. Mitigations are appropriate: job gated to dependabot[bot], gate script sparse-checked out from base ref (not PR head), persist-credentials: false, inline check-wait avoids third-party wait-for-check actions on elevated token.
.github/scripts/dependabot-anthropic-gate.mjs 12–18, 171–178, 209–227 info Cursor check trust uses name + app.slug + details_url host (cursor.com). Cross-app same-name spoofing and stale in-progress Cursor runs are handled well; tests cover spoofed apps and (app, name) dedup for non-Cursor checks.
.github/scripts/dependabot-anthropic-gate.mjs 29, 334–338, 549–553 info Review/comment evidence limited to cursor[bot] Bot actors; human echo of agent IDs cannot enter evidence.
.github/scripts/dependabot-anthropic-gate.mjs 446–477 info parseAnthropicDecision() fails closed on preamble, fences, extra keys, and non-bare JSON—good defense against echoed {"safe_to_merge":true} snippets in model output.
.github/workflows/dependabot-auto-merge.yml 76–80, 95–99, 117 info PR head drift re-checked before approve and auto-merge; gate outputs assessed_head_sha; merge uses --match-head-commit. Addresses TOCTOU between assessment and merge.
.github/scripts/dependabot-anthropic-gate.mjs 480–515 warning LLM final gate: cursor[bot] bodies and check output are still untrusted input to Anthropic. System prompt says not to treat them as instructions, but a determined prompt-injection in check text/comments could still bias safe_to_merge. This is a residual merge-policy risk, not page-world exploitability.
.github/scripts/dependabot-anthropic-gate.mjs 23, 232 warning Non-Cursor checks treat neutral (and skipped) as passing. If any required CI publishes neutral for a soft failure, the gate could proceed to Anthropic anyway. Confirm org CI never uses neutral for “failed but non-blocking.”
.github/workflows/dependabot-auto-merge.yml 81–84 warning Approve step ends with || true, so approval API failures are swallowed while later steps may still run (subject to GitHub merge rules). Prefer failing the job or checking gh api exit status so a green run reflects an actual approval.
.github/workflows/dependabot-auto-merge.yml 117–120 info Auto-merge enable failures exit 0 (intentional fail-soft). Combined with approve || true, a green workflow can mean “gate said merge” without approve/merge actually succeeding—monitor logs.

No findings against message-bridge trust boundaries, captured-globals, DDGProxy, or config-gated feature code (unchanged).


Risk Level

Low Risk (for injected runtime / page-world security): the diff does not modify content-scope injection code. Medium Risk for repository supply chain (automated npm approve + auto-merge with an LLM gate)—well-hardened for a pull_request_target workflow, but not equivalent to human review of dependency diffs.


Recommendations

  1. (warning) Remove || true on the approve gh api call, or assert exit status / re-fetch reviewDecision before auto-merge.
  2. (warning) Document or assert that no required check uses conclusion: neutral for failures; otherwise narrow PASSING_CHECK_CONCLUSIONS for the gate wait.
  3. (info) Consider requiring non-empty matchedCursorSources (or minimum check output text) for each expected Cursor check before calling Anthropic, so “success + empty evidence” cannot reach the model.
  4. (info) Add a workflow_dispatch / manual dry-run job that runs node --test .github/scripts/dependabot-anthropic-gate.test.mjs in CI (tests pass locally: 50/50).
  5. (info) After merge, confirm ANTHROPIC_API_KEY secret and Cursor check names match production (Cursor Automation: Web compat and sec, etc.).

Verdict: Safe to merge this meta PR from an injected-code perspective; monitor auto-merge behavior on the first real Dependabot npm PRs.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

- Run dependabot-anthropic-gate.test.mjs in CI (no tokens required)
- Reject blank Cursor evidence before calling Anthropic; poll up to 3m for
  cursor[bot] comments after checks complete
- Fail the approve step with API output instead of swallowing errors
- Serialize gate runs per PR via concurrency group
- Switch from pull_request_target to pull_request with in-repo fork guard

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>

@cursor cursor 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.

Stale comment

Web Compatibility Assessment

No injected-runtime findings. The diff at 825b65c0 is limited to .github/dependabot.yml, .github/workflows/dependabot-auto-merge.yml, .github/workflows/tests.yml, and .github/scripts/dependabot-anthropic-gate.{mjs,test.mjs}. There are no changes under injected/, messaging/, entry points, wrapper-utils.js, captured-globals.js, or feature code — nothing that alters API shims, prototype chains, DOM behavior, or per-platform injection.

File Severity Note
(none) Out of scope for page-level web-compat review

Security Assessment

No injected-runtime security findings. The hostile page threat model (captured globals, message bridge, nativeData, origin checks, config gating) does not apply to this diff.

File Severity Note
(none) No runtime trust-boundary changes

CI note (informational, not injected): The new gate script fail-closes on strict Anthropic JSON parsing, triple-signal Cursor check matching (name + app.slug + details_url host), cursor[bot]-only evidence, SHA pinning before approve/auto-merge, and npm-only + dependabot[bot] + same-repo head guards in the workflow. That is appropriate for supply-chain automation but is separate from content-script security.

Risk Level

Low Risk — This PR does not modify code shipped into web pages; it only changes Dependabot scheduling and GitHub Actions merge gating.

Recommendations

  1. No injected action required for web-compat or page-injection security on this PR.
  2. When this workflow later auto-merges dependency PRs, each of those should still receive a full injected review if the diff touches injected/ or related runtime paths (this automation path is npm-only + Cursor + Anthropic gated).
  3. Ensure ANTHROPIC_API_KEY remains org-scoped and rotated; a compromised key could approve merges, but only for Dependabot npm PRs that already passed CI and Cursor checks.
Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

@cursor cursor 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.

Web Compatibility Assessment

File Severity Finding
(none — no injected runtime changes) info This PR touches only .github/ (Dependabot config, auto-merge workflow, gate script + tests). There are no changes to injected/src/, wrapper-utils.js, captured-globals.js, message bridge, or platform entry points. Zero direct web-compatibility surface in page-facing code.
.github/workflows/dependabot-auto-merge.yml warning Indirect compat risk via merge policy: the old workflow auto-approved only semver-patch npm updates and excluded @atlaskit/pragmatic-drag-and-drop, preact, @preact/signals, and lottie-web. The new Anthropic gate applies to all npm_and_yarn updates with no semver or package denylist. Major/minor dependency bumps that alter injected bundles could reach main faster if the LLM gate misjudges Cursor evidence.

Security Assessment

File Lines Severity Finding
(scope) info No injected-runtime security changes. Assessment below covers CI/supply-chain automation only.
.github/scripts/dependabot-anthropic-gate.mjs 26–40, 198–206, 236–254 info Strong check-run trust model: Cursor evidence requires matching name + app.slug + details_url host (cursor.com). Non-Cursor checks dedupe by (app, name) so a later same-named success from another App cannot mask an earlier failure. Well-tested.
.github/scripts/dependabot-anthropic-gate.mjs 51–56, 361–405 info Comment trust boundary: only cursor[bot] Bot accounts are accepted; inline review comments (/pulls/{pr}/comments) are included so Bugbot inline findings aren't missed. Human commenters cannot inject evidence.
.github/scripts/dependabot-anthropic-gate.mjs 423–429 info Bugbot attribution falls back to head_sha substring matching (no agent id in Bugbot details_url). Safe because author is already restricted to cursor[bot].
.github/scripts/dependabot-anthropic-gate.mjs 531–562 info Strict Anthropic output parsing: requires a bare JSON object with exactly {safe_to_merge, reason, confidence} — rejects preamble/trailing prose/code fences/extra keys. Mitigates prompt-injection via echoed JSON in comment bodies.
.github/scripts/dependabot-anthropic-gate.mjs 84–87, workflow 82–86, 106–110 info Head-SHA drift guard: re-fetches PR head before approve/auto-merge; refuses stale evidence if the branch advanced during assessment.
.github/workflows/dependabot-auto-merge.yml 25–27, 29–35 info Fork guard + base-branch script checkout: runs only for same-repo Dependabot PRs; gate script is sparse-checked out from base branch with persist-credentials: false, so a malicious Dependabot PR cannot swap the gate logic on the assessed commit.
.github/workflows/dependabot-auto-merge.yml 11–20 info Explicit permissions includes actions: read (required for job-id exclusion) — correct per least-privilege with explicit block.
.github/scripts/dependabot-anthropic-gate.mjs 565–600 warning Probabilistic final gate: merge approval ultimately depends on an LLM interpreting truncated Cursor evidence (MAX_BODY_CHARS = 12000). Fail-closed on parse/CI/evidence errors, but a model false-positive on "safe_to_merge": true is still possible. This is supply-chain automation risk, not page-world exploit risk.
.github/scripts/dependabot-anthropic-gate.mjs 467–471 warning Check-run output alone satisfies evidence: hasActionableEvidence() accepts non-empty output.title/summary/text without matched cursor[bot] comments. Spoofing is mitigated by matchExpectedCheck(), but a Cursor check reporting success with a misleadingly terse summary (e.g. "Low Risk") could reach Anthropic without corroborating review text. Consider requiring matched sources for automation checks, not just Bugbot.
.github/workflows/dependabot-auto-merge.yml 73–95 warning Removed package/semver guardrails: prior patch-only + explicit blocklist for high-churn packages is gone. Re-introduce a deterministic denylist (or major-bump manual-only rule) before the LLM gate for packages with known injected breakage history.
.github/workflows/dependabot-auto-merge.yml 128–131 info Auto-merge enable failure exits 0 (intentional soft-fail). Approve step correctly exits 1 on failure — asymmetric but reasonable.

Risk Level

Low Risk for injected web compatibility and page-world security — no runtime code changes.

Medium Risk for repository supply-chain automation — LLM-gated auto-merge now covers all npm semver levels without the previous patch/package blocklist.

Recommendations

  1. (warning) Restore a deterministic pre-LLM denylist for @preact/signals, preact, lottie-web, @atlaskit/pragmatic-drag-and-drop, and optionally require manual review for semver-major bumps regardless of Cursor/Anthropic sign-off.
  2. (warning) Tighten hasActionableEvidence() for the two Cursor Automation: checks to require at least one matched cursor[bot] review/inline comment, not just check-run output summary text.
  3. (info) Consider logging the Anthropic reason + confidence as a workflow step summary (not PR comment) for post-merge auditability.
  4. (info) Gate script unit tests pass locally (55/55). CI job github-scripts-unit correctly gates the aggregate all-required job.

Cursor Automation: Web compat and sec

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

@dharb dharb 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.

Nice, looks good. Let's give it a shot 🚀

@jonathanKingston
jonathanKingston added this pull request to the merge queue Jun 8, 2026
Merged via the queue into main with commit 870610c Jun 8, 2026
42 checks passed
@jonathanKingston
jonathanKingston deleted the jkt/auto/content-scope-dependabot-591f branch June 8, 2026 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver-patch Bug fix / internal — no release needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants