v1.7.1 shipped on the same day as v1.7.0. That is not a coincidence. The v1.7.0 community release triggered a security audit pass that surfaced 1 CRITICAL and 5 HIGH vulnerabilities, and v1.7.1 closed every one of them within 24 hours. This is the story of the security debt that was hiding under v1.7.0's release celebrations, and how it got paid down before anyone noticed it was overdue.
The Trigger: Expanded Surface Area, Expanded Threat Model
v1.7.0 was the largest community release in the history of the project. New translation API integrations, NotebookLM workflows, Drive credential flows, and contributor-submitted skills doubled the third-party-code surface in a single release. Independently of those contributions, the v1.6.x credential-handling code had quietly accumulated subtle issues. They were the kind of issues that any individual code review would miss, but a focused pre-release audit catches in 20 minutes.
The audit ran against the v1.7.0 release candidate. Six findings came out of it: one CRITICAL, five HIGH. Every fix shipped in v1.7.1 the next day. Here is what was wrong and how it got fixed.
VULN-001 (CRITICAL): API Key Exposure via .mcp.json Tracking
The bug: setup_image_mcp.py wrote the user's Google AI API key to .mcp.json in the user's project directory. .mcp.json is not in the default .gitignore on most setups. If the user ran git add . and committed the file, the key landed in public repo history the moment they pushed.
The fix: setup_image_mcp.py now defaults to a --global flag that writes the key to ~/.claude/settings.json with file mode 0600 (owner read and write only, no group, no other). Project-local config is still available behind an explicit --local flag, which prints a warning and a suggested .gitignore entry on every invocation. The default is safe.
Who is affected: any user who ran image setup on v1.6.5 through v1.7.0 and committed .mcp.json. The advisory mitigation is rotate the Google AI key, scrub git history (the file may live in old commits even if it is gitignored now), and re-run setup on v1.7.1.
VULN-002 (HIGH): OAuth CSRF Without State Token
NotebookLM and Google API setup used standard OAuth web flows but did not validate the state parameter on callback. RFC 6749 section 10.12 is explicit: state is the canonical CSRF defense for OAuth. Without it, a malicious page could trigger a callback to localhost:port with attacker-supplied code, and the token-exchange code would proceed without checking that the callback corresponded to a request the user actually initiated.
The fix: generate a cryptographically random state token before constructing the auth URL, store it locally, and validate strictly on callback. Mismatches reject the token exchange with a clear error. The local listener is also now bound to 127.0.0.1 instead of 0.0.0.0, which prevents callback hijacking from other machines on the same network.
VULN-003 (HIGH): Hash-Pinned Dependencies Across 4 Lock Files
Supply-chain risk. pip install trusts whatever PyPI serves at install time. A compromised mirror, a typosquat that ships under a real package name briefly, or a hijacked maintainer account can ship malicious code that runs at install time.
The fix: hash-pin every dependency in requirements.txt and 3 nested skill requirements files. CI uses pip install --require-hashes. Hash mismatches now fail loud rather than silently installing whatever PyPI happens to serve. This closes the v1.6.x supply-chain attack surface entirely.
VULN-004 (HIGH): PowerShell 5.1 Compatibility Regression
Not a vulnerability in the classic sense, but a hardening reliability fix. install.ps1 used the PowerShell 7+ three-argument form of Join-Path, which silently fails on the Windows 10 and Windows 11 default of PowerShell 5.1. Users got truncated installs without errors. The skill appeared to install, then silently broke at first run.
The fix: rewrote the installer to use the two-argument Join-Path form plus explicit path validation after every join. Works correctly on PowerShell 5.1 and up across every install path. Verified on a clean Windows 11 image with PowerShell 5.1 only.
VULN-005 (HIGH): Frontmatter Completeness Enforcement
Discovered during the audit: 15 of 27 user-invokable SKILL.md files were missing one or more of description, argument-hint, or license in frontmatter. Not exploitable directly. But a missing license field creates ambiguous redistribution rights, which is a real legal-exposure issue when contributors fork and bundle skills.
The fix: a pytest invariant now requires all three fields on every user-invokable skill. CI blocks merges that violate. Existing skills were brought into compliance in the same PR that introduced the invariant.
VULN-006 (HIGH): Credentials File Permissions
Token cache files at ~/.cache/claude-blog/*.json were created with the default umask. On most Linux and macOS systems, the default umask produces mode 0644, which is world-readable. OAuth tokens are sensitive credentials. World-readable is the wrong choice.
The fix: explicit chmod 0600 after every credential write. Writes are now atomic via tempfile plus shutil.move, which closes the partial-write window where a token file could exist on disk with the wrong permissions for a few milliseconds before the chmod ran.
The 24-Hour Turnaround
Mechanical guardrails enforced via pytest invariants meant that any security-affecting change had to clear an automated bar before merge. The four invariants that made the turnaround possible:
- No agent grants Bash. Preserves the prompt-injection containment line. If an agent could shell out, prompt injection escalates to remote code execution.
- No invalid
allowed-toolsfield. Catches typos that silently grant Bash. A field name typo would silently fall back to default, which is overly permissive. - Unique skill names. Prevents shadowing attacks where a malicious contribution registers under the same name as a trusted skill.
- FLOW sync integrity. SHA-256 lockfile validates before write. Any unauthorized modification of the lockfile fails the invariant.
All v1.7.1 fixes passed the invariants on the first try, because the invariants existed before the audit. The lesson: write the guardrails before you need them. A security audit becomes a 24-hour fix sprint instead of a multi-week rewrite when the mechanical bar is already in place.
What This Means For Users
Update to v1.7.1. Specifically:
- Rotate any Google API key that may have been committed to git via
.mcp.json. - Re-run image setup if you used v1.6.5 through v1.7.0.
- Re-authorize Drive sessions and NotebookLM credentials. The previous tokens used permissive file modes and unprotected OAuth flows.
- If you use Claude Blog in CI, pin to v1.7.1 or later. Hash-pinned requirements are only effective if you pin the skill version too.
Conclusion
Security hardening sprints are unsexy. Nobody tweets about CRITICAL-to-zero in 24 hours the way they tweet about a new feature. But the work compounds. v1.7.1 closed 6 findings, and the next release inherits the higher baseline. Mechanical guardrails do the work that humans forget. Community submissions raise the bar on everyone, and the security review is the price of entry, not a punishment. Ship the guardrails first. The audit will find things. Make sure the things it finds can be fixed in a day.