Announcing Beacon: Self-improving memory for coding agents
← All skills
Skill

interactive-login

skill:yc-software/qm#interactive-login

Log a CLI in with a browser/device-code flow (aws sso, gh, glab, gcloud, …) and save the result to the keychain so later commands can request it through execute.credentials.

Verdict
Inconclusive

The analysis could not cover enough of the artifact to decide.

Analyzed Oct 27, 2026, 21:53 UTC at 9432ce5

Dispute security verdict

If you believe this verdict is incorrect, submit a dispute and we'll review it.

0
findings
—
highest severity
0
threat classes
2,607
files scanned
18/18
checks satisfied
0
package advisories

Why this verdict

  • inconclusive: gap invalid_judge_finding
  • inconclusive: gap skill_frontmatter_invalid at .claude/skills/dev-instance/SKILL.md
  • inconclusive: gap skill_frontmatter_invalid at .claude/skills/update-qm/SKILL.md
  • inconclusive: gap skill_frontmatter_invalid at .claude/skills/upstream-pr/SKILL.md
  • inconclusive: unresolved question on CRED
  • inconclusive: unresolved question on EXFIL

Security findings 0

No individual findings recorded

The inconclusive verdict rests on the open questions and coverage gaps below; 7 candidate findings were refuted on review.

Open questions 3

What the analysis could not settle from the code alone.

Where does AGENT_API_URL resolve at runtime for the keychain upload — a fixed agent-control-plane host, or an environment value that could point the credential upload at an attacker-controlled endpoint?
Data exfiltration
What service does AGENT_API_URL point to at runtime — is it the platform keychain that `execute.credentials` reads back, or an operator-settable destination that could receive the uploaded login files elsewhere?
Data exfiltration
Is the config copied into the fresh directory in step 1 sourced from the real `$HOME` (e.g., the real `.aws/config`), and does that content intentionally become part of the uploaded keychain credential?
Credential access
Refuted findings 7

Candidates the review stage examined and rejected, kept for transparency.

Data exfiltration

criticalRefutedlow confidence

save-login.mjs recursively reads every file under the caller-chosen `dir` argument, base64-encodes their contents, and POSTs the whole collection to the URL in the AGENT_API_URL environment variable, so any directory passed on the command line (the script performs no path validation) is uploaded wholesale to a network service; per the skill's declared keychain-save purpose the instructed source is a fresh mktemp login directory, making this a declared-core-capability exfil primitive rather than an established harm.

fetch(`${AGENT_API_URL}/v1/keychain/credentials`,
Why it was refuted

Reading the instructed login directory, base64-encoding it, and POSTing it to the env-configured keychain endpoint with the env capability token is exactly the declared keychain-save capability stated verbatim in SKILL.md; the unvalidated dir argument is a hardening gap, but the finding itself concedes the instructed source is always the fresh mktemp directory.

Destructive action

criticalRefutedmedium confidence

On a 400 or 413 keychain rejection save-login.mjs runs discard()'s rmSync against the staged login directory after the upload body was already sent, destroying the local login on a failed save, while fetch errors and other non-ok statuses leave the directory intact for retry and the line-55 rmSync runs only after a fully successful save.

if (response.status === 400 || response.status === 413)
Why it was refuted

The 400/413 discard deletes only the mktemp staging directory this same flow just created, and the skill's documented recovery for a failed login is starting again from step 1 with a new directory, so no user-owned data is ever in the deletion path; the undeclared failure-path removal is a robustness inconsistency, not DESTRUCT harm.

Credential access

criticalRefutedmedium confidence

The skill instructs the agent to copy real-home AWS config blocks from .aws/config into the sandbox directory that is subsequently uploaded in full, so configuration data read from the real $HOME is included in the off-host saved login, in tension with the skill's claim that nothing from the real $HOME is usable.

Copy in any config the CLI needs first (for AWS, the
Why it was refuted

Copying .aws/config blocks into the sandbox is instructed in the open and the same section declares the full-directory upload to the wrapped service's keychain, which is the declared core capability; D3 bars treating $HOME config as a usable login, not copying it as CLI bootstrap, so this is disclosed core behavior, not an undeclared credential channel.

Credential access

criticalRefutedlow confidence

SKILL.md instructs the agent to copy existing `.aws/config` profile and SSO-session blocks (which live in the real home) into the sandbox directory whose entire contents are then read, base64-encoded, and uploaded to the keychain endpoint, sending real-host AWS configuration to the network service as part of the saved login.

blocks in `.aws/config`); everything under that
Why it was refuted

Both the .aws/config copy instruction and the whole-directory base64 upload are stated on the surface of the skill, and posting the login directory to the env-configured keychain endpoint under the env capability token is the declared keychain-save capability, so no concealed real-host credential flow exists.

Data exfiltration

criticalRefutedmedium confidence

The skill instructs the agent to run the bundled save-login.mjs, which per detector leads collects the files under the login directory and POSTs them to the environment-defined AGENT_API_URL keychain endpoint with an env-sourced token header, sending credential material (AWS/GitHub/GitLab/GCP logins) off-host to a destination the text never names.

The script uploads only the files under that directory, removes the directory, and
Why it was refuted

The only network call in the shipped script is the declared core capability itself — saving the login directory to the wrapped service's keychain using its env API key — and SKILL.md declares the upload scope verbatim, so the EXFIL characterization reduces to documented core behavior.

Destructive action

criticalRefutedlow confidence

The skill forbids the agent from removing the login directory itself and to rerun the bundled script on failure, while the script force-removes directories recursively (rmSync recursive,force per detector leads), so credential-bearing data is destroyed by a step the text tells the agent not to manage, and a failed save may destroy the collected login before retry.

Never remove the directory yourself; if the save fails, run the script again.
Why it was refuted

The only deletion path the instructed flow reaches is the fresh mktemp staging directory the flow itself creates, whose removal the skill declares and whose loss is recovered by its own documented restart procedure; the 400/413-versus-rerun wording is a robustness wart, not destructive harm to any pre-existing asset.

Destructive action

highRefutedlow confidence

save-login.mjs recursively and forcibly deletes the directory given as its second command-line argument (on rejection in discard() and again after a successful upload) with no validation that the path is a temporary login directory, so a mistaken or injected argument irreversibly destroys arbitrary local files.

rmSync(dir, { recursive: true, force: true });
Why it was refuted

Every invocation the artifact instructs passes the fresh mktemp login directory as the argument, and the skill declares that the script removes that directory; nothing in the artifact chains any other path into dir, so the arbitrary-destruction scenario rests on speculative misuse outside the shipped chain.

Coverage gaps 19

Parts of the artifact the static analysis could not fully examine.

context:unanalyzable_binary binary content is only byte-scanned
desktop/assets/qm.icns
context:unanalyzable_binary pdf content is only byte-scanned
test/fixtures/documents/encrypted.pdf
context:unanalyzable_binary pdf content is only byte-scanned
test/fixtures/documents/hostile.pdf
context:unanalyzable_binary binary content is only byte-scanned
test/fixtures/documents/sample.doc
context:unanalyzable_binary pdf content is only byte-scanned
test/fixtures/documents/sample.pdf
context:unanalyzable_binary binary content is only byte-scanned
test/fixtures/documents/sample.ppt
context:unanalyzable_binary binary content is only byte-scanned
test/fixtures/documents/sample.pptx!/ppt/printerSettings/printerSettings1.bin
context:unanalyzable_binary binary content is only byte-scanned
test/fixtures/documents/sample.xls
context:unanalyzable_binary pdf content is only byte-scanned
test/fixtures/documents/scanned.pdf
skill_frontmatter_invalid missing name/description frontmatter
.claude/skills/dev-instance/SKILL.md
skill_frontmatter_invalid missing name/description frontmatter
.claude/skills/update-qm/SKILL.md
skill_frontmatter_invalid missing name/description frontmatter
.claude/skills/upstream-pr/SKILL.md
context:manifest_unparseable yaml: while parsing a flow node
deploy/helm/templates/deployment.yaml
context:manifest_unparseable yaml: while parsing a flow node
deploy/helm/templates/ingress.yaml
context:manifest_unparseable yaml: while parsing a flow node
deploy/helm/templates/pvc.yaml
context:manifest_unparseable yaml: while parsing a flow node
deploy/helm/templates/secret.yaml
context:manifest_unparseable yaml: while parsing a flow node
deploy/helm/templates/service.yaml
context:manifest_unparseable yaml: while parsing a flow node
deploy/helm/templates/serviceaccount.yaml
invalid_judge_finding request.0