Repository navigation
Run vulnerability remediation fallback agent with Pi and no write credentials - #31
Conversation
|
Live |
|
Picked ulziibay-kernel to review: touched the changed code in |
|
Pushed 5e4d780: the Pi job now references only the model keys. Revoking the minted token before Pi ran wasn't enough. Every secret a job references sits in
Tests (
Live self-test on 5e4d780 (run 42125023082) succeeded. Nothing was fixable, so |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5e4d780. Configure here.

Summary
Move the fallback agent in the reusable
vuln-remediation.ymlworkflow from Cursor CLI to Pi, and run it in a job that references no secrets except the model API keys.Before this change, the
fixjob minted an unscoped GitHub App token, persisted it in the checkout, and wrote it into a globalgit config url.<token>.insteadOfbefore the agent ran. The agent also gotSOCKET_SECURITY_API_KEY. In practice the agent could push to any branch of the caller repo.Revoking a minted token before the agent runs is not enough. Every secret a job references is delivered to the runner at job start and stays in
Runner.Workermemory for the whole job. Hosted runners have passwordless sudo, so an agent in that job could read the app private key from memory and mint its own token. The job that runs Pi therefore references onlyANTHROPIC_API_KEYandOPENAI_API_KEY.Jobs
scanandtriageare unchanged apart from SHA pinning. After them:fixADMIN_APP_*(read token),SOCKET_API_TOKENbun installandsocket fixwith a read-only token scoped to the caller repo. The token is passed through per-stepGIT_CONFIG_*env vars and never written to global git config. Uploads Socket's diff as a patch. If Socket left no diff, it runsgo mod downloadfor the Go modules named in the fixes and uploads only$GOMODCACHE/cache/download/github.com/kernel. Never runs the agent.agentANTHROPIC_API_KEY,OPENAI_API_KEYsetup-go/setup-buncaches so the agent can't poison caches that other workflows restore. Runs Pi, then uploads tracked changes, committed or not, against the base as a patch.verifySOCKET_API_TOKENvalidate-diff, the post-fix Socket plan,confirm, andsummarize-fix.prADMIN_APP_*validate-diff(allowlist) and rejects anything other than regular non-executable files. Commits, then mints acontents/pull-requestswrite token. Pushes with--force-with-leaseagainst the branch SHA recorded before the agent ran, and creates or updates the PR as before. Never runs repo code, package managers, Socket, or Pi.The workflow sets
permissions: contents: read, so the caller'sGITHUB_TOKENis read-only in every job. All actions are pinned to SHAs.Agent invocation
agent -p --model $CURSOR_PREFERRED_MODEL ...becomespi --print --no-session --approve --model "$PI_PREFERRED_MODEL" --thinking max @prompt. This is the same setup as the other Pi workflows: Node 22,npm install -g --ignore-scripts @earendil-works/pi-coding-agent,PI_SKIP_VERSION_CHECK=1, andPI_TELEMETRY=0..github/workflows/vuln-remediation/fix-prompt.mdatsecurity-workflows-ref, rendered withenvsubstinto$RUNNER_TEMP, and passed with@file.CURSOR_*reference is left in this workflow.github.com/kernel/*modules not already in the module cache can't be downloaded.triage-prompt.mdis not used by this workflow and is untouched.Callers
All 21 caller repos use
secrets: inherit, and none has private git dependencies inpackage.json. No caller changes are needed.Validation
actionlintpasses on both changed workflows. With shellcheck enabled, the only finding is a pre-existing SC2086 in the untouchedscanjob.python3 -m unittest scripts.test_vuln_remediation: 17 tests pass.scripts/test_vuln_remediation_workflow.py(17 tests) runs in the self-testtestsjob.piis the only agent job, and itssecrets.*set is exactly the two model keys. No other job runs Pi.fix,verify, andpreach reference only the secrets listed in the table above. Actions are pinned, checkouts don't persist credentials, agent-job caches are off, and the publish job mints its token last.fixandagentare executed against temp git repos.verifyandprgate scripts are executed on fresh clones. They reject source edits (validate-diffallowlist exit) and executable files. The publish mode check rejects a symlinked lockfile thatvalidate-difflets through. Both reject a patch that doesn't match its file list, and a valid lockfile change is committed onsecurity/vuln-remediation.SOCKET_API_TOKENorADMIN_APP_PRIVATE_KEYback into the Pi job;fixdownload step on a fixture that requiresgithub.com/kernel/hypeman-go, then the realagentrestore step into an empty module cache.go buildsucceeded with allgithub.comgit access redirected to an invalid host. Without the restored subtree, the same build fails.openai/gpt-5.6-sol, and a fresh Pi config dir, against an npm fixture withminimist@1.2.5. Pi bumped it to 1.2.6 in about 40 seconds without committing or staging. The snapshot,validate-diff,confirm, and publish apply/commit steps then passed on its output.workflow_dispatchofvuln-remediation-self-test.ymlwithrun-live-remediation=trueon the latest commit (run 42125023082) succeeded.scan,triage, andfixpassed. Socket found no fixable alerts in this repo, soagent,verify, andprwere skipped. Pi has not run on GitHub Actions yet. The first scheduled caller run where Socket can't fix an alert will be the first live Pi run.Risks and not verified
PI_PREFERRED_MODELset.kernel-images-private, which kernel/kernel requires) still can't be downloaded. That was already true before this change. Widening it toowner: kernelwith contents read would make them work and would still keep the token out of the agent job.verifyruns without a GitHub token, the same as thetriageplan. If it fails, confirmation fails closed and no PR is opened.ACTIONS_RUNTIME_TOKEN(artifacts and cache) and a read-onlyGITHUB_TOKEN, as every job does. It could upload a stray artifact, which makesverify's upload fail closed. It could also write Actions cache entries directly; disabling the setup-action caches removes only the default path for that.Follow-ups
CURSOR_API_KEY/CURSOR_PREFERRED_MODEL. I left it alone because other workflow migrations in this repo touch the same lines.