Skip to content

fix: Keep URL signing key and API token out of responses and logs - #1508

Merged
jirispilka merged 9 commits into
masterfrom
claude/relaxed-albattani-j7v8s4
Oct 9, 2026
Merged

jirispilka merged 9 commits into
masterfrom
claude/relaxed-albattani-j7v8s4

Conversation

@jirispilka

@jirispilka jirispilka commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Found urlSigningSecretKey in tool responses. With the key, anyone holding the transcript can sign non-expiring links to the whole storage. The same key also leaked via resources/read and apify-api-read, and the user's API token leaked via resources/read and server logs.

Part of apify/ai-team#330.

What changed

Where Before After
get-dataset, get-key-value-store "urlSigningSecretKey": "Xq9…" field removed; signed *PublicUrl fields stay
resources/read, apify-api-read on storage JSON "urlSigningSecretKey": "Xq9…" "urlSigningSecretKey": "[REDACTED]", every other byte unchanged
resources/read /v2/browser-info "authorization": "Bearer apify_api_…" "authorization": "Bearer [REDACTED]"
logHttpError (all callers) raw axios error, incl. config.headers.Authorization name, message, stack, code, type, cause only

The proxies redact instead of removing the key, because removal means re-serializing the JSON. Re-serializing changes formatting and number precision, and a crafted body cost about 1 GB per read. Hosted logs no longer include fields like status, config.url or errno.

Notes for reviewers (human-written)

This started as the two-tool fix for (get-dataset, get-key-value-store). While fixing it we found the same key in resources/read and apify-api-read, then the API token in resources/read (/v2/browser-info) and in server logs (logHttpError). In hindsight it should have been several PRs 🤦. Splitting it now costs more than it saves, so it ships as one. Most of the codes are tests only though.

Proof it works

  • Live mcpc probe on real storages (RESTRICTED and public, deleted afterwards): no signing key or token in any output; "[REDACTED]" on /v2/datasets/{id}, /v2/key-value-stores/{id} and run-storage shortcuts.
  • pnpm run test:integration: the new storage cases pass on stdio, streamable and stateless HTTP.
  • AI-assisted (Claude Code).

🤖 Generated with Claude Code

https://claude.ai/code/session_011ezU64HVKM4BD7jMtSzqag


Generated by Claude Code

claude and others added 7 commits October 8, 2026 10:44
Work in progress for apify/ai-team#330, still under review.

- get-dataset and get-key-value-store drop urlSigningSecretKey.
- apify-api-read and resources/read remove the key from JSON bodies.
- resources/read masks the session token in every body.

Claude-Session: https://claude.ai/code/session_011ezU64HVKM4BD7jMtSzqag
Work in progress for apify/ai-team#330, still under review.

- apify-api-read and resources/read redact the urlSigningSecretKey value
  instead of removing the key; no parse or re-serialization.
- Drop the superseded removal code and its tests.

Claude-Session: https://claude.ai/code/session_011ezU64HVKM4BD7jMtSzqag
Part of apify/ai-team#330.

- readApiResource logs failed requests (including the signed-link
  fallback) without the axios request config, which holds the token.
- Share one token mask and the plain-error helper between both proxies.
- Redact a value with a backslash before a line break.
- Docs: redaction applies to JSON bodies decoded with their declared
  charset; apify-api-read description no longer claims byte-exact bodies.
- Drop an unneeded cast and comments that restated the code.

Claude-Session: https://claude.ai/code/session_011ezU64HVKM4BD7jMtSzqag
Part of apify/ai-team#330.

logHttpError logged the raw error, so an axios error carried its request
config, including the Authorization header, into the log for every caller.
It now logs a plain copy (name, message, stack, code, type, cause) and
still picks the log level from the original error.

Claude-Session: https://claude.ai/code/session_011ezU64HVKM4BD7jMtSzqag
Part of apify/ai-team#330.

Copy only Error causes, at most three levels deep, so a cyclic or very
deep cause chain cannot make the logger throw. Log non-Error values
without a synthetic stack or their fields.

Claude-Session: https://claude.ai/code/session_011ezU64HVKM4BD7jMtSzqag
Part of apify/ai-team#330.

Log a primitive cause as text, as before; still drop object causes,
which can hold a request config. Pin the cause depth with a test.

Claude-Session: https://claude.ai/code/session_011ezU64HVKM4BD7jMtSzqag
@jirispilka
jirispilka marked this pull request as ready for review October 8, 2026 20:47
@apify-service-account apify-service-account added the tested Temporary label used only programatically for some analytics. label Oct 8, 2026
@apify-service-account apify-service-account added the t-ai Issues owned by the AI team. label Oct 8, 2026
@jirispilka
jirispilka requested review from MQ37 and RobertCrupa October 8, 2026 20:48
claude added 2 commits October 9, 2026 06:54
Part of apify/ai-team#330.

Remove tests for inputs the API never sends (very deep nesting, bodies
past the inline limit, timing), regex cases on invalid JSON, unchanged
signed-link log levels, and cause-depth/primitive-cause details. Every
key-removal, redaction, token-mask and log-leak test stays and still
fails on the old code.

Claude-Session: https://claude.ai/code/session_011ezU64HVKM4BD7jMtSzqag

@RobertCrupa RobertCrupa left a comment

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.

Looks good! I tested it on my end and no keys or token could be extracted

@jirispilka
jirispilka merged commit ce7098c into master Oct 9, 2026
16 checks passed
@jirispilka
jirispilka deleted the claude/relaxed-albattani-j7v8s4 branch October 9, 2026 19:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-ai Issues owned by the AI team. tested Temporary label used only programatically for some analytics.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants