This document describes Claudemeter's security posture and how to report issues.
Please email security reports to security@hyperi.io. Do not open public
GitHub issues for vulnerabilities. We aim to acknowledge within 72 hours
and to ship a fix or formal mitigation within two weeks for high-severity
issues.
The marketplace artifact dist/extension.js is built from extension.js
via node esbuild.js --production.
Claudemeter has zero runtime dependencies (package.json
dependencies is empty). It ships no browser, no Chromium, and no
scraping stack - usage is fetched with the platform fetch from the
first-party api.anthropic.com OAuth endpoints. The only third-party
packages are dev-only (esbuild, @vscode/vsce, eslint, vitest),
which never reach an installed extension.
(For historical context: earlier versions drove a browser to harvest a
claude.ai session cookie - first puppeteer-core (whose
@puppeteer/browsers → proxy-agent → pac-proxy-agent → get-uri → basic-ftp chain was a recurring advisory source, stripped via a
build-time stub), then playwright-core. Both are gone: the switch to
Claude Code's OAuth token removed the browser entirely, and with it the
whole runtime-dependency surface.)
We track every Dependabot / npm-audit advisory and dismiss with a reason rather than letting alerts pile up. Current accepted risks:
Reaches us via @vscode/vsce → @azure/identity → @azure/msal-node → uuid. Status: deferred - tolerable risk.
- Reach: dev-only.
@vscode/vsceis invoked bynpm run packageand the GitHub Actions release pipeline. It never reaches an installed extension at runtime. - Vulnerable code path:
uuid.v3 / v5 / v6with a user-controlledbufargument.@azure/msal-nodecallsuuid.v4()(random generation, no buffer argument), so the vulnerable branch is never reached on our call paths. - Why we can't fix forward:
@azure/msal-nodedeclares"uuid": "^8.3.0"as a hard dependency, so a top-leveloverridesto bump uuid past 8.x would break msal-node's own usage. - Re-evaluation trigger: we re-check whenever
@azure/msal-nodepublishes a new minor that bumps its uuid pin, or whenever@vscode/vsceswitches its auth backend off@azure/identity.
The 4 GitHub Dependabot alerts for this advisory (one per package in
the chain) have been dismissed with reason tolerable_risk and a
comment pointing to this section.
Policy: no advisory stays open without a decision. When Dependabot fires:
- If the audit says
npm audit fixresolves it, ship the lockfile fix and let GitHub auto-close the alert on next scan. - If the audit says
--force(semver-major downgrade or breaking change), open a draft change and weigh the regression risk against the actual reach. Don't accept the downgrade without a written justification in the commit body. - If neither (1) nor (2) applies - the chain is genuinely stuck on
upstream - dismiss the alert with one of the GitHub-provided
reasons (
tolerable_risk,not_used,inaccurate) and a comment that links here, so a future maintainer can find the rationale.
- Claudemeter reads Claude Code's existing OAuth token from the shared
store it writes - the macOS Keychain (
Claude Code-credentials) or~/.claude/.credentials.jsonon Linux/Windows (honouringCLAUDE_CONFIG_DIR). It reads the token fresh per fetch and never copies, persists, or writes it back - so it cannot disturb Claude Code's own login. - The token is used only as the
Authorization: Bearerheader tohttps://api.anthropic.com. Requests setredirect: 'error'so a Bearer-bearing request is never followed to another host. - Claudemeter never refreshes the token. Claude Code owns the token lifecycle, and Anthropic rotates refresh tokens. Claudemeter only tracks the current value, so it cannot invalidate Claude Code's session.
- Debug logs (rotated under the config dir, max 256 KB by default) and the Dump State report never include the token - only its presence, source, scopes, and expiry. If you find a log that leaks a token, please report per the section above.
The debug log and the Dump State report are built to be pasted into a public GitHub issue. Redaction is automatic. By design they carry:
- No token or credentials - only token presence, source, scopes, expiry.
- No account name and no email - the state dump reports account
presence plus org type (e.g.
Personal), nothing identifying. (src/oauthFetcher.js's CLI smoke-test also redacts name/email.) - No username in paths - every logged line goes through
scrubHome()(src/utils.js), which swaps the home directory for~, so absolute paths never carry the OS username.
What does appear, deliberately: plan/tier strings, usage percentages, token counts, timestamps, and your own project folder names. The gauge is project-scoped, so a folder name is often what pins down a "wrong project" report.
The redaction is automatic, so the output should be safe to post as-is. We do not warrant every environment though - read it before you attach it, and cut anything you would rather not share. On our side it is a maintained invariant. A change that adds identifying data to a log or the state dump is a defect - report it.