Skip to content

[AI-52] feat: Pin aikido as an external plugin - #228

Open
SaintPatrck wants to merge 2 commits into
mainfrom
feat/vendor-aikido-plugin
Open

[AI-52] feat: Pin aikido as an external plugin#228
SaintPatrck wants to merge 2 commits into
mainfrom
feat/vendor-aikido-plugin

Conversation

@SaintPatrck

@SaintPatrck SaintPatrck commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

https://bitwarden.atlassian.net/browse/AI-52

📔 Objective

Pins aikido (AikidoSec/aikido-claude-plugin) as an external plugin entry in marketplace.json at commit 1353c9d. No files are copied into this repo; the entry references the upstream repository at a fixed commit, with a new "External plugins" README section for this category.

Note

HEAD -1 sha pinned to facilitate testing of the Renovate config in #232

@SaintPatrck SaintPatrck added the ai-review Request a Claude code review label Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

🤖 Bitwarden Claude Code Review

Overall Assessment: REQUEST CHANGES

Re-reviewed the SHA-pinned aikido marketplace entry and the "External plugins" README section; the diff is still limited to .claude-plugin/marketplace.json (+15) and README.md (+8), with no third-party files vendored into this repo and no credential, settings, hook, agent, or command material touched. The pin concern raised on the previous run is closed from my side: the author confirmed it is deliberate, the PR description now records that HEAD-1 was pinned to exercise the Renovate config in the follow-up PR, and audit-external-plugin.yml re-runs the external-plugin audit against whatever SHA the entry carries — so 868d7ce is being audited by this PR rather than inheriting the earlier pin's audit. The one open item is CI: Validate / Validate (workflow "Validate AI Configuration") reports failure on head commit 6577cce, and this review could not read the job log, so the cause is unconfirmed. Worth confirming it is unrelated to this change before merge — aikido is the first entry in marketplace.json whose source is a remote object rather than a ./plugins/<name> path, and the validators documented in .claude/CLAUDE.md walk marketplace entries expecting a local plugin directory.

Code Review Details
  • ⚠️ : Required "Validate AI Configuration" check is failing on the head commit; cause not verifiable from this review path (gh run view / gh api unavailable here)

Notes considered and not raised as findings: the deliberately-stale pin and the missing Renovate custom manager were answered by the author and are covered by the per-PR audit workflow, so that thread is not reopened; no plugin under plugins/ changed, so the version-bump and changelog requirement in .claude/CLAUDE.md does not apply; the entry's omitted version field is consistent with a remote source carrying its own plugin.json version; the url + sha source shape was verified end to end by the author and the resolution mechanism is unchanged by the new SHA; the vendor security assessment is carried by the parent PR in this stack (#227, VULN-984) and was not re-litigated; Lint, Aikido, and CLA all pass, and the README table formatting is prettier-clean.

Comment on lines +116 to +120
"source": {
"source": "url",
"url": "https://github.com/AikidoSec/aikido-claude-plugin.git",
"sha": "1353c9d54b387f259f0c04f0ed3408842203c29e"
},

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.

QUESTION: Does Claude Code resolve a url source with a sha key, and does installing land on 1353c9d?

Why this is worth confirming before merge

The entire security value of this entry is the pin, so it's worth verifying that Claude Code actually honors this shape rather than ignoring the unrecognized keys.

Two things make it hard to confirm from the repo alone:

  • Every git-backed source Claude Code records locally uses the git discriminator with the clone URL — e.g. ~/.claude/plugins/known_marketplaces.json stores {"source": "git", "url": "https://github.com/bitwarden/ai-plugins.git"} — not url.
  • The marketplace cited as precedent in the README section (anthropics/claude-code) currently contains only relative-path entries, so there's no third-party url + sha example there to compare against.

If either key falls outside the plugin-source schema, the likely failure mode is silent: unknown fields get dropped and installs track upstream main, which is the opposite of what the new README section promises.

An end-to-end check settles it:

/plugin marketplace add <this branch>
/plugin install aikido@bitwarden-marketplace

then confirm the recorded gitCommitSha for aikido@bitwarden-marketplace in ~/.claude/plugins/installed_plugins.json is 1353c9d54b387f259f0c04f0ed3408842203c29e.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed end to end: added this branch as a local marketplace, ran claude plugin install aikido@bitwarden-marketplace, and ~/.claude/plugins/installed_plugins.json records "gitCommitSha": "1353c9d54b387f259f0c04f0ed3408842203c29e" and "version": "1.2.7" — the url + sha source resolves and installs exactly on the pinned commit, no silent fallback to upstream main.

prograhamming
prograhamming previously approved these changes Sep 3, 2026
@SaintPatrck
SaintPatrck force-pushed the feat/vendor-aikido-plugin branch from 28259f2 to 6528958 Compare September 4, 2026 15:37
@SaintPatrck
SaintPatrck marked this pull request as ready for review September 4, 2026 15:50
@SaintPatrck
SaintPatrck requested a review from a team as a code owner September 4, 2026 15:50
An error occurred while trying to automatically change base from feat/auditing-external-claude-plugins to main September 4, 2026 17:10
"source": {
"source": "url",
"url": "https://github.com/AikidoSec/aikido-claude-plugin.git",
"sha": "868d7ce08e6ee6fc4fbb539a07d2ec7bc1d7482d"

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.

⚠️ IMPORTANT: The pin moved off the audited commit to an older release, and nothing in this repo will move it forward.

Details

6528958 pinned 1353c9d, described in its own message as "the same commit already validated by the auditing-external-claude-plugins skill", and confirmed end to end in the thread above. a3dee6e repoints the entry at 868d7ce (1.0.20) with the rationale that "pinning the commit before it gives the Renovate custom manager a real digest change to propose rather than a no-op" — a tooling-ergonomics reason, not a review outcome.

Two consequences of merging as-is:

  1. The new README section promises entries are "pinned to a reviewed commit". If the audit covered 1.0.21 (1353c9d) and not 1.0.20 (868d7ce), that claim no longer holds for the only entry in the table, and installs land one release behind the audited code.
  2. The Renovate custom manager the change is staged for does not exist on this branch. .github/renovate.json sets "enabledManagers": ["github-actions", "npm"] and defines no customManagers, and with enabledManagers set, a custom manager only runs when custom.regex is listed there. So no digest PR will be proposed, and the deliberately-stale pin stays until someone lands that manager.

If the intent is to have a pending bump ready for the Renovate work, consider landing the custom manager first (or in this PR) and pinning to the audited commit here. If 868d7ce was itself audited, saying so in the PR description would settle it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional. See the note in the PR description.

Base automatically changed from feat/auditing-external-claude-plugins to main September 4, 2026 17:22
@SaintPatrck
SaintPatrck dismissed prograhamming’s stale review September 4, 2026 17:22

The base branch was changed.

prograhamming
prograhamming previously approved these changes Sep 4, 2026
Adds the aikido plugin (AikidoSec/aikido-claude-plugin) as a SHA-pinned
external entry at commit 1353c9d, the same commit already validated by
the auditing-external-claude-plugins skill. Introduces a separate
'External plugins' README section, mirroring how Anthropic's own
marketplace presents third-party entries: not copied into this repo,
referenced at a fixed commit.
Upstream main is at the 1.0.21 merge, so pinning the commit before it
gives the Renovate custom manager a real digest change to propose rather
than a no-op.
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

aikido

Security Audit: aikido-claude-plugin (vendoring candidate)

Audited artifact: https://github.com/AikidoSec/aikido-claude-plugin.git @ 868d7ce08e6ee6fc4fbb539a07d2ec7bc1d7482d (2026-08-16, merge of "mcp-1.0.20"). Plugin aikido v1.2.6 (MIT). Launches bundled MCP server @aikidosec/mcp@1.0.20 (AGPL) via npx -y from the npm registry; tarball sha512 (base64) dKokuk9CEHjFvV0pqpoxraHSF6TpiVVVkjWC+A47hCQiPFv3xBwXOR045ZEtObnQWceazOsb29LVgW6xGKyHzg==. The package bundles 8 platform gitleaks binaries (SHA-256 recorded below) and downloads Opengrep v1.10.0 and Checkov 3.2.395 binaries at first server start.
Method: gather-evidence.sh (git clone at pinned SHA, npm pack @aikidosec/mcp@1.0.20, tarball extraction, npm audit against the shipped npm-shrinkwrap.json, npm attestation fetch); beautify.sh on dist/index.js (916 KB minified → 54,284 lines) followed by a full trace of every Aikido-authored module (tool registration, all seven tool handlers, auth/keychain, region/API client, temp-dir writer, gitleaks/opengrep/checkov runners, binary/rules downloaders, login loopback server); shasum -a 256 on all bundled binaries; review of all repo files (manifests, 3 skills, .mcp.json, README).
Not done: Dynamic execution of the plugin or server (static review only). file/strings/go version identification of the bundled gitleaks binaries — the audit sandbox blocked those commands on the scratch directory, so the gitleaks version and build provenance are unverified (SHA-256 hashes recorded for later comparison: linux_x64 9cb7ae0e…, linux_arm64 23178226…, linux_x32 ed36463f…, mac_arm64 55b0522a…, mac_x64 33e8dcbc…, win_arm64 0253fcb3…, win_x32 bc8a9281…, win_x64 2071eab0…). ATTESTATIONS_UNAVAILABLE: npm returned 404 for attestations — the package must be treated as unsigned/no-provenance. The server's TypeScript source is not published in the plugin repo; review was of the built bundle only. Vendor libraries inside the bundle (~90% of lines: zod, MCP SDK, pino, node-fetch, decompress chain, jose) were sampled, not read line-by-line. Whether @github/keytar 7.10.6 runs native install scripts during npx install was not verified. No legal/license review.


1. Executive summary

Overall risk: Medium
Recommendation: Go with conditions

What the plugin actually does at runtime (verified from the bundle, not the README):

  • Registers a stdio MCP server via npx -y @aikidosec/mcp@1.0.20 (exact-version pin; transitive deps locked by shipped npm-shrinkwrap.json).
  • Registers 4 tools by default: aikido_issues_list (cloud read), aikido_full_scan (local scan + cloud metadata POST), aikido_login (browser sign-in / keychain write), aikido_ignore_issue (cloud write — marks issues ignored in the org's Aikido feed). Three more scan tools are gated behind AIKIDO_MCP_ALL_TOOLS=true.
  • Scans run locally: file contents supplied by the model are written to a mkdtemp dir (path-traversal-checked, mode 0600), scanned by bundled gitleaks / downloaded Opengrep / downloaded Checkov, then deleted.
  • Scan findings leave the machine: SAST finding code snippets, file paths, line numbers, and repo name are POSTed to api/ide-plugin/sast/issue-metadata; secrets findings (redacted secret with last 4 chars visible, matched line, absolute temp path, fingerprint) are POSTed to api/ide-plugin/secrets/issue-metadata. The cloud response decides which findings the user sees (server-side ignore lists).
  • At every server start it fetches Opengrep v1.10.0 and Checkov 3.2.395 binaries from GitHub releases (two of five from Aikido's own checkov-binary-builder repo) with no checksum or signature verification, plus an Opengrep rules zip from the Aikido API, and executes them.
  • Auth: bearer token from AIKIDO_API_KEY env or OS keychain (@github/keytar, service aikido-mcp); browser OAuth-style loopback flow on 127.0.0.1 with a 32-byte random state. All API hosts are hardcoded HTTPS (ide.aikido.dev, bg.us/me/au.aikido.dev) except a dev-mode escape hatch (AIKIDO_DEV_MODE=truehttp://app.local.aikido.io).

Conditions for Go:

  1. Document (or gate) aikido_ignore_issue: vendored install guidance should require explicit user confirmation before this tool is invoked (F-02), e.g. via permission settings that deny it by default.
  2. Surface the data-egress behavior to installing teams: scan findings (including code snippets and last-4 characters of detected secrets) are sent to Aikido's cloud (F-04). Teams scanning sensitive Bitwarden source must accept this or not install.
  3. Track the fail-open secrets-scan behavior (F-01) as an upstream issue with AikidoSec; until fixed, treat "no findings" from aikido_full_scan as unverified when network errors are plausible.
  4. Resolve the AGPL question for the runtime-fetched server before vendoring (Section 6).
  5. Assign ownership for re-pinning: the plugin pins @aikidosec/mcp@1.0.20 exactly; each upstream bump requires re-audit.

2. Findings

Severity scale: Critical / High / Medium / Low / Info. CWE mapped where meaningful.

F-01 (Medium) Secrets scan fails open when the cloud metadata API fails

Where: dist/index.js (beautified pretty.js ~45892–45922 Ck, ~45960–45963 hG): after gitleaks finds secrets locally, results are POSTed to api/ide-plugin/secrets/issue-metadata; on any non-401 HTTP error or JSON parse failure the function logs and returns [], and the tool reports zero findings ("[]").
Risk: CWE-754/CWE-390. A network outage, proxy interference, or a 5xx from Aikido silently converts "secrets detected" into "scan clean". The agent (and the scan skill's fix-and-rescan loop) will conclude generated code is free of leaked secrets when it is not. This is a security-relevant check failing open.
Remediation: Upstream fix: propagate the error (as the 401 path already does) or return the locally-detected findings with local-only metadata, mirroring the SAST fallback (K1). Until then, document that network errors can mask secret findings.

F-02 (Medium) Remote write tool aikido_ignore_issue registered by default with no gate

Where: pretty.js 54120 (registration in FT), ~46780–46802 (IT, PUT api/ide-plugin/issues/{id}/ignore).
Risk: CWE-862 (missing authorization gate at the tool layer). This is the required tool-permission-scope flag: a state-mutating tool that suppresses issues in the organization's Aikido security feed is enabled by default, gated only by having a valid token, with no confirmation step and no read-only alternative for the mutation. A prompt-injected or misdirected agent can silently mark real vulnerabilities as ignored (with any fabricated "reason"), degrading the org's security posture in a way that is hard to notice. The optional scan tools are env-gated, but this write tool is not.
Remediation: In the vendored configuration, deny mcp__aikido-mcp__aikido_ignore_issue by default (Claude Code permission rules) or require explicit user approval per call. Upstream: gate it behind an env flag like the extra scan tools, or require an explicit confirmation argument.

F-03 (Medium) Runtime download-and-execute of scanner binaries with no integrity verification

Where: pretty.js 45514–45618 (Opengrep from github.com/opengrep/opengrep/releases/.../v1.10.0/*), 54155–54238 (Checkov from github.com/bridgecrewio/checkov/releases/download/3.2.395/* and github.com/AikidoSec/checkov-binary-builder/releases/download/ve44b38e9e9a1ba9444cae00902b06e19876315bf/*); rules zip from api/ide-plugin/sast/rules (39345–39353). All extracted with the bundled decompress and executed.
Risk: CWE-494 (download of code without integrity check). URLs are version-pinned and HTTPS, but GitHub release assets are mutable by the repo owner — a compromised opengrep, bridgecrewio, or AikidoSec GitHub account yields arbitrary code execution on every developer machine at next server start, with no hash to detect the swap. Two of five Checkov builds are Aikido's own rebuilds, not the upstream vendor's. Additionally, the cached binary in the per-user cache dir (~/.cache/aikido-mcp / platform equivalent) is reused on subsequent startups with no re-verification, so any local process that can write to the user cache gains persistent code execution inside the scanner (a modest local-escalation persistence vector).
Remediation: Upstream: embed expected SHA-256 digests for each pinned release asset and verify before execution; verify the cached binary hash on reuse. For Bitwarden: note that first server start requires outbound access to github.com and executes third-party binaries.

F-04 (Medium) Unconditional auto-trigger sends code-derived findings to a third-party cloud

Where: skills/scan/SKILL.md line 3: "Always run an Aikido scan after generating code to verify the generated code is free of security issues." Combined with G1 (pretty.js 39354–39383, POSTs sast_issues including snippet — actual source lines — plus file paths and repo_name) and Ck (secrets metadata POST including redacted secret with last 4 characters visible, matched line, and absolute temp path).
Risk: Data leaves the machine by default, on every code-generation turn, without per-invocation consent: fragments of proprietary source code (finding snippets) and partial secret material (last-4 chars + gitleaks fingerprint + surrounding line) flow to Aikido's cloud, keyed to the org's account. Under a passive-observer (honest-but-curious vendor / their logging pipeline) threat model this is a confidentiality exposure independent of any compromise. The file contents being scanned stay local — only findings egress — but findings are, by construction, the most sensitive lines.
Remediation: Vendored README must state plainly what egresses and when. Teams working on Critical-classified code should require explicit invocation rather than the "always run" auto-trigger (Bitwarden could patch the skill description on vendoring to remove the unconditional clause).

F-05 (Low) SAST/IaC findings are dropped when neither cloud nor local metadata is available; cloud controls visibility

Where: pretty.js 39317–39336 (K1 fallback keeps only rules present in the cached rules_metadata/metadata.json; bx returns {} on read failure), 45681–45692 / 46170–46181 (tG/kG: cloud enrichment first, fallback on error); server response ignore flags filter findings (jk, 45929–45948).
Risk: CWE-754. If the metadata POST fails and the local rules cache is missing, locally-detected SAST/IaC findings silently vanish (partial fail-open, less severe than F-01 because the rules cache normally exists once setup succeeded). Separately, by design the Aikido cloud decides which local findings the developer sees — a compromised or misconfigured tenant can suppress findings client-side.
Remediation: Upstream: report unenriched findings with a generic title rather than dropping them. Awareness item for users otherwise.

F-06 (Low) Known-vulnerable pinned dependencies in the shipped shrinkwrap

Where: npm audit of the published package: 8 advisories — decompress@4.2.1 (direct; GHSA-mp2f-45pm-3cg9 Critical, GHSA-h39j-r5qq-r9mm, GHSA-jwp9-9v96-94mx; no fixed version exists), fast-uri (High ×6), ip-address (High), hono/@hono/node-server/qs/body-parser/express-rate-limit (Moderate/Low).
Risk: CWE-1104. Reachability is limited: the bundled decompress copy includes the realpath/symlink escape guards, but the hardlink path (fs.link(linkname, …) at pretty.js 45506) still creates archive-controlled hardlinks — exploitable only via a malicious archive, which per F-03 already implies a compromised source with a stronger primitive. The hono/qs/body-parser advisories sit in HTTP-server code paths that the stdio transport never starts.
Remediation: Upstream should replace decompress (unmaintained, no fix available) with a maintained extractor and refresh the shrinkwrap. Not a vendoring blocker given reachability.

F-07 (Low) Remote content rendered into agent context steers agent behavior and code edits

Where: skills/scan/SKILL.md step 4 ("Apply fixes guided by the remediation provided by Aikido", loop up to 3 times); skills/issues/SKILL.md (renders issue_title/issue_remediation from the feed); aikido_login tool response PT (pretty.js 46676) embeds imperative instructions to the AI ("Do NOT alter the URL… Do NOT suggest alternative tools…") and a URL the agent must relay verbatim.
Risk: Prompt-injection surface: issue titles/remediation text originate from the Aikido tenant (which ingests repo names, dependency names, and other attacker-influenceable strings) and from Aikido's servers; the scan skill explicitly instructs the agent to modify code based on that text. A compromised tenant or server could steer the agent into malicious "fixes" or relay a phishing sign-in URL. This is inherent to the MCP design and mitigated by the agent's judgment, but it is a real boundary: tool output here is instructions, not just data.
Remediation: Note in the vendored README that scan remediation text is remote content and code fixes derived from it deserve the same review as any other diff. No repo-level fix available short of upstream sanitization.

F-08 (Low) No npm provenance; npx -y fetch-and-execute at session start

Where: .mcp.json (npx -y @aikidosec/mcp@1.0.20); attestation fetch returned 404 (unsigned package).
Risk: CWE-494/supply chain. The exact-version pin plus npm's version immutability and the shipped shrinkwrap make this a reasonable pin, but there is no content hash and no provenance attestation: an npm registry compromise or a takeover of the @aikidosec scope that could serve altered content for an existing version would execute on developer machines. npx also installs the 173 locked production deps (including native @github/keytar), so install scripts in that tree run at first launch.
Remediation: Ask upstream to publish with npm provenance (--provenance). Bitwarden's pin-audit process (this audit at a fixed commit + version) is the compensating control; keep re-audit mandatory on version bumps.

F-09 (Low) Dev-mode env var downgrades API transport to plain HTTP

Where: pretty.js 12644–12650 (AIKIDO_DEV_MODE === "true" → base http://app.local.aikido.io), used by gr() for all API calls including Authorization: Bearer headers; also the loopback sign-in delivers the token as a URL query parameter (?token=…) to http://127.0.0.1 (46549–46575), which can persist in browser history.
Risk: CWE-319 (dev path only). Anyone who controls the environment of the developer's session and DNS for app.local.aikido.io can harvest the bearer token; requires local influence, so Low. Token-in-query on the loopback is standard-but-imperfect OAuth-loopback practice; browser history retains the one-time token, which remains valid until rotated.
Remediation: Awareness only; upstream could POST the token to the loopback instead, and refuse AIKIDO_DEV_MODE outside a dev build.

F-10 (Info) Manually pasted token stored without validation and without awaiting the write

Where: pretty.js 46693–46696 (KG: R1(t) — floating promise, no await, no server-side verification) vs. the tool description's claim that an invalid-looking token returns an error.
Risk: A garbage or attacker-supplied token is written to the OS keychain and reported as "signed in"; the keychain write can also race the success message. Failure surfaces later (scans validate via V1 and fail closed), so impact is confusion, not compromise.
Remediation: Upstream: await R1(t) and call V1(t) before confirming storage.


3. Checked and found clean

  • Plugin manifests (.claude-plugin/plugin.json, .claude-plugin/marketplace.json, .mcp.json): well-formed; no hooks, no commands, no extra permissions; mcpServers points only at the exact-pinned npm package. (Required manifest check: clean.)
  • MCP transport: stdio (StdioServerTransport); no network transport, no WebSocket/SSE listener. The only listening socket is the ephemeral 127.0.0.1 loopback during browser sign-in, bound to localhost, single-use, state-checked (32 random bytes, base64url), timed out at 5 minutes, HTML responses properly escaped.
  • Credential storage: OS keychain via @github/keytar (service aikido-mcp, account auth-token); no plaintext token file fallback; keychain read errors throw rather than degrade.
  • API transport: all four region hosts hardcoded HTTPS (ide.aikido.dev, bg.us/me/au.aikido.dev); sign-in portal hosts hardcoded HTTPS app*.aikido.dev. Region is routed from an unverified JWT claim, but only selects among the four hardcoded HTTPS hosts, so no redirect primitive.
  • Auth failure mode: default-registered scan/list/ignore tools are wrapped by an auth check (Dn()); unauthenticated calls fail closed with a sign-in instruction. Token validation (V1) fails closed on non-OK, bad payload, and 10s timeout.
  • Temp-dir handling / path traversal (CWE-22): model-supplied relativeFilePath values are rejected if absolute or containing .., joined path is prefix-checked against the mkdtemp root, files written mode 0600, temp dir deleted in finally and its name pattern re-checked before rmSync.
  • Scanner process execution: gitleaks/opengrep/checkov invoked via spawn with array args (no shell interpolation); gitleaks runs --no-git --max-target-megabytes=1 with the bundled config; checkov runs --skip-download (no external module fetch during scan).
  • Scan content locality: the code files being scanned are written only to the local temp dir; file contents are never uploaded — only finding metadata/snippets (see F-04).
  • Secret redaction before egress: detected secrets are redacted to ****…<last4> (≤10-char secrets fully replaced) before the metadata POST.
  • Hardcoded secrets: none in the plugin repo (3 skills, manifests, README) and none observed in any Aikido-authored module of the bundle; gitleaks.aikido.toml is a detection ruleset (regex patterns), not credentials.
  • Symlinks: symlinks.txt and pkg/symlinks.txt both report 0 — no symlinks in the repo or the published package; nothing pointing outside the audited tree.
  • Dependency pinning: .mcp.json pins the exact server version; the package ships npm-shrinkwrap.json locking all 173 production deps; overrides pin hono/@hono/node-server floors.
  • Full-scan error transparency: aikido_full_scan uses Promise.allSettled and reports per-scanner errors in an errors object rather than hiding partial failures (the fail-open in F-01 is inside the secrets pipeline, not the aggregator).
  • Logging: pino to stderr only; no log files with sensitive content.

4. Data classification and trust boundary (P01–P06)

Data touched Direction Bitwarden classification Notes
Source code under scan (full contents) Model → local temp dir only Internal–Critical (depends on repo) Never uploaded; deleted after scan; mode 0600
SAST finding snippets, file paths, line/col, repo name Local → Aikido cloud (HTTPS, bearer) Confidential The most security-sensitive lines of the code, by construction (F-04)
Detected secrets (redacted, last 4 chars + fingerprint + matched line + absolute temp path) Local → Aikido cloud Critical (partial secret material) Redaction is good but not total (F-04)
Aikido bearer token Browser → 127.0.0.1 loopback → OS keychain; sent to *.aikido.dev APIs Critical (credential) Keychain storage; env-var override; dev-mode HTTP caveat (F-09)
Aikido feed issues (titles, remediation, SLA) Aikido cloud → agent context Confidential + prompt-injection surface Remote text steers agent behavior (F-07)
Ignore mutations (issue id + reason) Agent → Aikido cloud Confidential / integrity-relevant Default-enabled write (F-02)
Scanner binaries + rules GitHub releases / Aikido API → local cache, executed n/a (supply chain) No integrity verification (F-03)

Principles engaged: P05 (Controlled Access) is the dominant concern — scan findings and partial secret material cross to a third party under an honest-but-curious observer model, and access is controlled by the Aikido tenant, not the user alone. P06 (Minimized Breach Impact) applies to the bearer token (keychain storage, rotation via re-login are adequate) and to F-03's persistence vector (cached unverified binaries extend any one-time compromise). P01 (Zero-Knowledge) is not directly implicated — no vault data is touched — but its spirit applies if Bitwarden source containing security-sensitive logic is scanned: the vendor sees finding snippets. P02–P04 are not engaged (no vault, no lock state).

Vendoring does not internalize the trust boundary: the marketplace would vendor only the thin plugin definition (skills + .mcp.json), while the actual code executing on developer machines is still fetched from npm at session start and fetches further binaries from GitHub at runtime. The trust boundary versus using the plugin externally is unchanged; what vendoring adds is Bitwarden's implicit endorsement plus control of the pin, which is why the pinned-version audit and re-pin ownership matter more than the repo contents themselves.


5. Recommended shape of the vendored plugin

  1. Keep the exact pin (@aikidosec/mcp@1.0.20 and commit 868d7ce) and record the tarball sha512 from this audit next to the pin, so any registry-side content change for the same version is detectable.
  2. Ship a default permission posture that requires user approval for mcp__aikido-mcp__aikido_ignore_issue (deny/ask by default) — it is the only remote-write tool (F-02).
  3. Amend or annotate skills/scan/SKILL.md to remove/soften the "Always run an Aikido scan after generating code" auto-trigger, or add a README warning that every scan sends finding snippets and partial secret material to Aikido's cloud (F-04). If skills are vendored verbatim, put the warning in the marketplace README entry.
  4. Document runtime egress and downloads in the vendored README: outbound to registry.npmjs.org (server install), github.com (Opengrep/Checkov binaries, first start), *.aikido.dev (auth, rules, findings metadata); binaries execute without integrity verification (F-03).
  5. Document the fail-open caveat (F-01): a clean secrets-scan result is only trustworthy when the Aikido API was reachable; treat network-error runs as inconclusive.
  6. Note the env-var surface: AIKIDO_API_KEY (token via env — visible to other user processes), AIKIDO_MCP_ALL_TOOLS, AIKIDO_DEV_MODE (plain-HTTP dev endpoint), LOG_LEVEL.
  7. File upstream issues with AikidoSec for F-01 (fail-open), F-02 (gate the write tool), F-03 (checksum-verify downloads), F-06 (replace decompress), F-08 (publish with npm provenance), and F-10 (validate pasted tokens), and track them against the next re-pin.

6. Open questions for a human

  1. Licensing: the plugin repo is MIT, but the runtime server @aikidosec/mcp is AGPL. Bitwarden does not redistribute the server (users fetch it via npx), but does listing a plugin whose mandatory runtime component is AGPL meet Bitwarden's OSS-policy bar? Needs legal sign-off.
  2. Re-pin ownership: who owns re-auditing and bumping the @aikidosec/mcp pin when Aikido releases updates (their MCP moves fast; 1.0.20 was merged 2026-08-16)? Stale pins also mean stale scanners (Opengrep 1.10.0, Checkov 3.2.395 are frozen by the bundle).
  3. Data-processing agreement: is Aikido an approved vendor for receiving code-finding snippets and partial secret material from Bitwarden developer machines (Section 4)? Which Aikido region/tenant would Bitwarden teams use?
  4. gitleaks binary provenance: the 8 bundled binaries could not be identified in this environment (file/strings blocked); their SHA-256 hashes are recorded in the header. Someone with an unrestricted shell should confirm what gitleaks version/build they are and whether they match a known upstream or Aikido build.
  5. @github/keytar install scripts: confirm whether the npx install of the shrinkwrapped tree executes native build/postinstall scripts on developer machines, and whether that is acceptable in locked-down dev environments.
  6. Upstream responsiveness: does AikidoSec commit to fixing F-01/F-02/F-03? Their answer should influence whether "Go with conditions" holds at the next re-pin.

prograhamming added a commit that referenced this pull request Sep 9, 2026
…install path

The Usage blocks followed "Install the plugin and invoke the agent:" with no
label, so they read as commands to run rather than example prompts. Reframe the
lead-in the way the other plugin READMEs do.

Prerequisites also pointed `aikido` installs at the `claude-plugins-official`
marketplace. Per PR #228 the plugin is pinned as an external entry in this
marketplace instead, so name the upstream author and give the actual install
command. Depends on #228 landing for that command to resolve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants