Repository navigation
Conversation
Exception data is what reaches LLM-generated fixes, and it routinely
carries secrets: `connect ECONNREFUSED postgres://admin:hunter2@…`,
provider errors echoing API keys, `?token=` in request URLs, cookies and
bearer tokens in serialized headers. Redaction previously covered only
custom attributes (and only top-level, email-only, in the browser), so
messages, stack traces, span status messages, third-party spans, relayed
browser events and anything an old SDK or plain OTel sender shipped went
to storage and the sink verbatim.
SDKs (scrub before anything leaves the customer's process, on by default):
- runtime-node: scrub exception messages/stacks/status messages in
captureException/captureMessage (incl. pre-init fallbacks), process
spans, withLlmCall/trackLlmCall errors, LlmCallHandle.setAttributes and
instrumentLlmClient; add a RedactingSpanExporter so every exported span
(HTTP URLs, headersToSpanAttributes, third-party instrumentations,
app-level recordException) is scrubbed; relay scrubs browser
message/stack/name/nested context (new `redact` option); new redactText
- runtime-browser: scrub messages, stacks, names and nested context; new
`redact: { keys, values } | false` option and scrubText export
(3.43 kB -> 4.14 kB brotlied, budget 5 kB)
- python adapter: new stdlib-only redact.py; caught-exception sampler
scrubs tracebacks with it
Patterns: add Basic auth, Authorization/Cookie/Set-Cookie header text,
connection strings with empty user or `@` in the password, sk-proj/
sk-ant, Stripe, GitHub fine-grained, GitLab, npm, SendGrid, Google keys,
password=/?token=/?api_key= assignments, Luhn-valid cards; keys for any
*authorization header, session/sessionid/sid, pwd, dsn, bounded ssn.
Custom value patterns now apply globally. Email and URL-scheme
quantifiers are bounded (email was quadratic on long strings: ~1 s per
32 KB, now ~10 ms).
Ingester (second line of defence for old SDKs / third-party OTLP senders,
before ClickHouse and the sink that feeds LLM fixing): scrub occurrence
message/stack/route/errorType, span names, deep browser context and
LLM-call attributes with the shared patterns; extra patterns via
AUTTER_REDACT_VALUE_PATTERNS / AUTTER_REDACT_KEY_PATTERNS.
Parity: test-vectors/redaction.json runs against node, browser, ingester
and Python; CI now runs the Python adapter tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖 AI authorship56.2% of changed code was AI-written — 0% human, 44% untracked, across 1,736 attributed line(s). By model: Most AI-authored files:
Provenance measured at authorship time by the autter CLI. |
🚦 Pre-merge checks ·
|
| Check | Status | Explanation |
|---|---|---|
| Too many lines changed | This PR changes 1975 lines (limit 1000). Splitting into smaller, focused PRs makes review more reliable. | |
| PII stored without application-level encryption | 3 potential issue(s) detected (max risk 87/100): CHANGELOG.md:8, packages/otlp-ingester/README.md:69, test-vectors/redaction.json:107. | |
| Repetitive boilerplate (duplicated block) | 1 potential issue(s) detected (max risk 30/100): packages/otlp-ingester/src/normalize-browser.ts:74. | |
| API key in source | 1 potential secret(s) detected (max risk 92/100): packages/runtime-node/test/fixtures/e2e-redaction.mjs:50. | |
| Database connection URL with embedded credentials | 13 root-cause issue(s) detected (max risk 100/100): adapters/python/test_redact.py:52, test-vectors/redaction.json:6, test-vectors/redaction.json:51, test-vectors/redaction.json:61, test-vectors/redaction.json:103. | |
| Co-change coupling | 2 file(s) changed without a partner they usually change with. | |
| Code duplication / DRY violation | 1 finding(s) on changed lines. | |
| Release Notes Curator | 1 potential issue(s) detected (max risk 33/100): packages/otlp-ingester/src/redact.ts:97. |
✅ Passed checks (195)
| Check | Status | Explanation |
|---|---|---|
| Too many files changed | ✅ Passed | Changed 32 file(s), within the limit of 50. |
| Too many unrelated chapters | ✅ Passed | 6 chapter(s) detected, within the limit of 6. |
| Generated files hiding real changes | ✅ Passed | Generated-file volume (0 lines) does not obscure the 1975 hand-written line(s). |
| Missing PR context | ✅ Passed | PR context looks sufficient. |
| Mixed concerns (refactor + behavior change) | ✅ Passed | The PR is one feature: secret scrubbing across the SDKs and the ingester. The edits to existing files (for example the context.ts deletions and the normalize-browser.ts rewiring) swap local redaction for the shared scrubber, which is part of that feature and is not a standalone no-behavior refactor. Tests, docs and ... [truncated 37 chars] |
| Migration + app logic + UI combined in one PR | ✅ Passed | The diff has no database migrations and no user-interface changes. It touches SDK and ingester logic, tests, docs and CI only. |
| Sensitive data in logs | ✅ Passed | No sensitive data in logs issues remaining after verification. |
| Log injection | ✅ Passed | No log injection issues detected. |
| Missing audit logging | ✅ Passed | No missing audit logging issues detected. |
| Removed observability | ✅ Passed | No removed observability issues remaining after verification. |
| Silent exception swallowing | ✅ Passed | No silent exception swallowing issues detected. |
| Unhandled promise rejection | ✅ Passed | No unhandled promise rejection issues detected. |
| Circuit breaker not detected | ✅ Passed | No circuit breaker not detected issues detected. |
| Stack trace leakage | ✅ Passed | No stack trace leakage issues detected. |
| Multi-write without detected transaction | ✅ Passed | No multi-write without detected transaction issues detected. |
| Possible TOCTOU in critical path | ✅ Passed | No possible toctou in critical path issues detected. |
| Idempotency key not detected | ✅ Passed | No idempotency key not detected issues remaining after verification. |
| Possible non-atomic read-modify-write | ✅ Passed | No possible non-atomic read-modify-write issues detected. |
| Optimistic locking not detected | ✅ Passed | No optimistic locking not detected issues remaining after verification. |
| Rate limiting not detected | ✅ Passed | No rate limiting not detected issues detected. |
| Rate limiting removed | ✅ Passed | No rate limiting removed issues detected. |
| Batch size limit not detected | ✅ Passed | No batch size limit not detected issues remaining after verification. |
| Pagination not detected | ✅ Passed | No pagination not detected issues detected. |
| Prompt injection risk | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| LLM output used in a dangerous sink | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| Sensitive data in prompt or system-prompt leakage | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| Over-privileged LLM tool / excessive agency | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| Missing validation on an LLM-driven decision | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| Unbounded LLM usage (denial-of-wallet) | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| Table exposed without row-level security | ✅ Passed | No row-level-security-related code touched by this diff. |
| Over-broad row-level security policy | ✅ Passed | No row-level-security-related code touched by this diff. |
| Code path that bypasses row-level security | ✅ Passed | No row-level-security-related code touched by this diff. |
| Privileged database credential reachable from the client | ✅ Passed | No row-level-security-related code touched by this diff. |
| Privileged query without row-level scoping | ✅ Passed | No row-level-security-related code touched by this diff. |
| Row-level security enabled with no policy | ✅ Passed | No row-level-security-related code touched by this diff. |
| Template-default gradient styling | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Interchangeable AI marketing copy | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Placeholder content shipped to users | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Emoji standing in for an icon system | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Call-to-action that goes nowhere | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Templated page composition | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Color outside the brand palette | ✅ Passed | No added frontend markup or brand-relevant style changes in this diff. |
| Hardcoded style bypassing design tokens | ✅ Passed | No added frontend markup or brand-relevant style changes in this diff. |
| Typography outside the brand type system | ✅ Passed | No added frontend markup or brand-relevant style changes in this diff. |
| One-off UI instead of the shared component | ✅ Passed | No added frontend markup or brand-relevant style changes in this diff. |
| Copy that does not match brand voice | ✅ Passed | No added frontend markup or brand-relevant style changes in this diff. |
| Screen does not match the rest of the product | ✅ Passed | No added frontend markup or brand-relevant style changes in this diff. |
| Merge-blocking marker left in the change | ✅ Passed | No pending-work markers added by this diff. |
| Known-defect marker shipped in code | ✅ Passed | No pending-work markers added by this diff. |
| Untracked TODO without an issue reference | ✅ Passed | No pending-work markers added by this diff. |
| Test disabled or left pending | ✅ Passed | No pending-work markers added by this diff. |
| PII in logs | ✅ Passed | No pii in logs issues detected. |
| PII or internals leaked in error response | ✅ Passed | No pii or internals leaked in error response issues detected. |
| User data stored without retention controls | ✅ Passed | No user data stored without retention controls issues detected. |
| PII sent to external / cross-border destination | ✅ Passed | No pii sent to external / cross-border destination issues detected. |
| Lockfile resolution / integrity tampered | ✅ Passed | No lockfile resolution / integrity tampered issues detected. |
| Dependency runs install-time lifecycle script | ✅ Passed | No dependency runs install-time lifecycle script issues detected. |
| Possible dependency-confusion attack | ✅ Passed | No possible dependency-confusion attack issues detected. |
| Lockfile resolves a dependency the manifest does not declare | ✅ Passed | No lockfile resolves a dependency the manifest does not declare issues detected. |
| Checked-in build artefact modified without source change | ✅ Passed | No checked-in build artefact modified without source change issues detected. |
| Dockerfile build-step is insecure | ✅ Passed | No dockerfile build-step is insecure issues detected. |
| External artefact pulled in without integrity pinning | ✅ Passed | No external artefact pulled in without integrity pinning issues detected. |
| Changed export, importer not updated | ✅ Passed | No changed export with an un-updated importer detected. |
| Missing linked tracker issue | ✅ Passed | No missing linked tracker issue issues detected. |
| Missing CODEOWNERS reviewer approval | ✅ Passed | No missing codeowners reviewer approval issues remaining after verification. |
| Missing security-team review on sensitive path | ✅ Passed | No missing security-team review on sensitive path issues remaining after verification. |
| Source changes without matching tests | ✅ Passed | No source changes without matching tests issues detected. |
| Migration missing rollback / down step | ✅ Passed | No migration missing rollback / down step issues detected. |
| Frontend importing database client directly | ✅ Passed | No frontend importing database client directly issues detected. |
| Route handler bypassing service layer | ✅ Passed | No route handler bypassing service layer issues detected. |
| Backend service importing UI module | ✅ Passed | No backend service importing ui module issues detected. |
| Cross-context internals import | ✅ Passed | No cross-context internals import issues detected. |
| Workspace package rule violation | ✅ Passed | No workspace package rule violation issues detected. |
| Inconsistent logging pattern | ✅ Passed | No inconsistent logging pattern issues detected. |
| Inconsistent error handling | ✅ Passed | No inconsistent error handling issues remaining after verification. |
| Endpoint missing input validation | ✅ Passed | No endpoint missing input validation issues detected. |
| Multi-write without transaction wrapper | ✅ Passed | No multi-write without transaction wrapper issues detected. |
| New feature shipped without feature flag | ✅ Passed | No new feature shipped without feature flag issues detected. |
| Module placed in the wrong workspace package | ✅ Passed | No module placed in the wrong workspace package issues detected. |
| Hallucinated import (package not installed) | ✅ Passed | No hallucinated import (package not installed) issues remaining after verification. |
| Nonexistent package (not found in registry) | ✅ Passed | No nonexistent package (not found in registry) issues detected. |
| Call to function that does not exist | ✅ Passed | No call to function that does not exist issues detected. |
| Generic placeholder identifier in production logic | ✅ Passed | No generic placeholder identifier in production logic issues remaining after verification. |
| Overbroad try/catch swallowing all exceptions | ✅ Passed | No overbroad try/catch swallowing all exceptions issues detected. |
| TODO / FIXME on critical path | ✅ Passed | No todo / fixme on critical path issues detected. |
| Comment contradicts or fabricates code behaviour | ✅ Passed | No comment contradicts or fabricates code behaviour issues detected. |
| Abstraction defined but never used | ✅ Passed | No abstraction defined but never used issues detected. |
| Code style differs from rest of codebase | ✅ Passed | No code style differs from rest of codebase issues detected. |
| Established pattern ignored | ✅ Passed | No established pattern ignored issues detected. |
| Unhandled edge case (null / empty / zero / boundary) | ✅ Passed | No unhandled edge case (null / empty / zero / boundary) issues detected. |
| Insecure training-data / doc-example pattern | ✅ Passed | No insecure training-data / doc-example pattern issues detected. |
| Dead code (defined but never referenced) | ✅ Passed | No dead code (defined but never referenced) issues detected. |
| Deprecated API call | ✅ Passed | No deprecated api call issues detected. |
| API pattern from wrong library version | ✅ Passed | No api pattern from wrong library version issues detected. |
| API endpoint removed | ✅ Passed | No api endpoint removed issues detected. |
| HTTP method changed (GET ↔ POST etc.) | ✅ Passed | No http method changed (get ↔ post etc.) issues detected. |
| New required field added to request | ✅ Passed | No new required field added to request issues detected. |
| Field removed from response schema | ✅ Passed | No field removed from response schema issues detected. |
| Response field type changed | ✅ Passed | No response field type changed issues detected. |
| HTTP status code changed | ✅ Passed | No http status code changed issues detected. |
| Auth requirement added / removed / changed | ✅ Passed | No auth requirement added / removed / changed issues detected. |
| Error response shape changed | ✅ Passed | No error response shape changed issues detected. |
| Pagination behaviour changed | ✅ Passed | No pagination behaviour changed issues detected. |
| Outbound webhook payload schema changed | ✅ Passed | No outbound webhook payload schema changed issues detected. |
| GraphQL field removed without deprecation | ✅ Passed | No graphql field removed without deprecation issues detected. |
| GraphQL enum value removed | ✅ Passed | No graphql enum value removed issues detected. |
| Vendor API consumer | ✅ Passed | This PR does not touch call sites of an open vendor API change. |
| SQL injection | ✅ Passed | No sql injection issues detected. |
| Cross-site scripting (XSS) | ✅ Passed | No cross-site scripting (xss) issues detected. |
| Path traversal | ✅ Passed | No path traversal issues detected. |
| Command injection | ✅ Passed | No command injection issues detected. |
| Insecure deserialization | ✅ Passed | No insecure deserialization issues detected. |
| Weak cryptography | ✅ Passed | No weak cryptography issues detected. |
| Insecure randomness for security material | ✅ Passed | No insecure randomness for security material issues detected. |
| Unsafe file upload | ✅ Passed | No unsafe file upload issues detected. |
| Missing input validation | ✅ Passed | No missing input validation issues detected. |
| Unsafe CORS configuration | ✅ Passed | No unsafe cors configuration issues detected. |
| Unsafe / open redirect | ✅ Passed | No unsafe / open redirect issues detected. |
| Missing CSRF protection | ✅ Passed | No missing csrf protection issues detected. |
| Unsafe cookie / session settings | ✅ Passed | No unsafe cookie / session settings issues detected. |
| Sensitive data exposure | ✅ Passed | No sensitive data exposure issues detected. |
| Invisible Unicode / Trojan Source characters | ✅ Passed | No invisible unicode / trojan source characters issues detected. |
| Access token in source | ✅ Passed | No access token in source detected. |
| Cloud credential in source | ✅ Passed | No cloud credential in source detected. |
| Webhook signing secret in source | ✅ Passed | No webhook signing secret in source detected. |
| OAuth client secret in source | ✅ Passed | No oauth client secret in source detected. |
| JWT signing secret in source | ✅ Passed | No jwt signing secret in source detected. |
| Hardcoded password | ✅ Passed | No hardcoded password detected. |
| pull_request_target executes untrusted PR code | ✅ Passed | No pull_request_target executes untrusted pr code issues detected. |
| Repository secrets exposed to fork PRs | ✅ Passed | No repository secrets exposed to fork prs issues detected. |
| Overbroad GITHUB_TOKEN / pipeline permissions | ✅ Passed | No overbroad github_token / pipeline permissions issues detected. |
| Third-party action pinned by mutable tag | ✅ Passed | No third-party action pinned by mutable tag issues detected. |
| Shell injection via workflow expression | ✅ Passed | No shell injection via workflow expression issues detected. |
| Cache poisoning risk from fork PRs | ✅ Passed | No cache poisoning risk from fork prs issues detected. |
| Deployment workflow changed | ✅ Passed | No deployment workflow changed issues detected. |
| Environment protection rule weakened | ✅ Passed | No environment protection rule weakened issues detected. |
| Auth middleware removed from route | ✅ Passed | No auth middleware removed from route issues detected. |
| Route protection changed (protected → public) | ✅ Passed | No route protection changed (protected → public) issues detected. |
| Permission / RBAC check removed | ✅ Passed | No permission / rbac check removed issues detected. |
| Required role weakened | ✅ Passed | No required role weakened issues detected. |
| Admin-only route exposed to lower privilege | ✅ Passed | No admin-only route exposed to lower privilege issues detected. |
| Token validation skipped in middleware chain | ✅ Passed | No token validation skipped in middleware chain issues detected. |
| JWT verification weakened or changed | ✅ Passed | No jwt verification weakened or changed issues detected. |
| Session expiration / TTL changed | ✅ Passed | No session expiration / ttl changed issues detected. |
| Password reset flow changed | ✅ Passed | No password reset flow changed issues detected. |
| OAuth callback / redirect handling changed | ✅ Passed | No oauth callback / redirect handling changed issues detected. |
| Webhook endpoint missing signature verification | ✅ Passed | No webhook endpoint missing signature verification issues detected. |
| Public route touches private/PII data | ✅ Passed | No public route touches private/pii data issues detected. |
| Table dropped (data loss) | ✅ Passed | No table dropped (data loss) issues detected. |
| Column dropped (data loss) | ✅ Passed | No column dropped (data loss) issues detected. |
| Column type narrowed (truncation risk) | ✅ Passed | No column type narrowed (truncation risk) issues detected. |
| Constraint removed | ✅ Passed | No constraint removed issues detected. |
| Index dropped | ✅ Passed | No index dropped issues detected. |
| NULL → NOT NULL (backfill required) | ✅ Passed | No null → not null (backfill required) issues detected. |
| NOT NULL → NULL (constraint relaxed) | ✅ Passed | No not null → null (constraint relaxed) issues detected. |
| Column default changed | ✅ Passed | No column default changed issues detected. |
| Primary key changed (FK cascade risk) | ✅ Passed | No primary key changed (fk cascade risk) issues detected. |
| Foreign key changed | ✅ Passed | No foreign key changed issues detected. |
| Unique constraint removed (duplicate risk) | ✅ Passed | No unique constraint removed (duplicate risk) issues detected. |
| Enum value removed | ✅ Passed | No enum value removed issues detected. |
| Column renamed without compatibility alias | ✅ Passed | No column renamed without compatibility alias issues detected. |
| Table renamed without compatibility view | ✅ Passed | No table renamed without compatibility view issues detected. |
| Long-locking migration operation | ✅ Passed | No long-locking migration operation issues detected. |
| CREATE INDEX without CONCURRENTLY | ✅ Passed | No create index without concurrently issues detected. |
| CONCURRENTLY inside a transaction | ✅ Passed | No concurrently inside a transaction issues detected. |
| New enum value used in the same transaction | ✅ Passed | No new enum value used in the same transaction issues detected. |
| Foreign key added without NOT VALID | ✅ Passed | No foreign key added without not valid issues detected. |
| Foreign key column is not indexed | ✅ Passed | No foreign key column is not indexed issues detected. |
| Table created without a primary key | ✅ Passed | No table created without a primary key issues detected. |
| Timestamp without time zone | ✅ Passed | No timestamp without time zone issues detected. |
| Money stored as a float | ✅ Passed | No money stored as a float issues detected. |
| Volatile default rewrites the table | ✅ Passed | No volatile default rewrites the table issues detected. |
| CHECK constraint added without NOT VALID | ✅ Passed | No check constraint added without not valid issues detected. |
| Row-level security policy column is not indexed | ✅ Passed | No row-level security policy column is not indexed issues detected. |
| UNIQUE column still allows many NULLs | ✅ Passed | No unique column still allows many nulls issues detected. |
| CONCURRENTLY index is not retry-safe | ✅ Passed | No concurrently index is not retry-safe issues detected. |
| Frontend performance issue | ✅ Passed | No additional explanation was reported. |
| Frontend security issue | ✅ Passed | No additional explanation was reported. |
| Frontend correctness issue | ✅ Passed | No additional explanation was reported. |
| Accessibility issue | ✅ Passed | No additional explanation was reported. |
| Frontend maintainability issue | ✅ Passed | No additional explanation was reported. |
| Code correctness issue | ✅ Passed | No code correctness issue issues remaining after verification. |
| Runtime error risk | ✅ Passed | No runtime error risk issues remaining after verification. |
| Resource leak risk | ✅ Passed | No additional explanation was reported. |
| Data integrity risk | ✅ Passed | No additional explanation was reported. |
| Maintainability issue | ✅ Passed | No maintainability issue issues remaining after verification. |
| Redundant alias / duplicate import | ✅ Passed | No additional explanation was reported. |
| Redundant type construct | ✅ Passed | No additional explanation was reported. |
| Unnecessary type assertion | ✅ Passed | No additional explanation was reported. |
| Module smell | ✅ Passed | No additional explanation was reported. |
| Dead export (no callers) | ✅ Passed | No additional explanation was reported. |
| AI authorship | ✅ Passed | 56.2% AI-authored (claude:claude-opus-5-5: 56%), 0% human, 44% untracked. |
| Intent vs. implementation | ✅ Passed | The visible portion of the diff delivers the PR's central promise of scrubbing secrets from messages, stacks, span names, LLM fields and browser context in the ingester. The SDK-side work sits in files omitted from the digest, so it could not be checked. Requirements: 6 implemented. (Intent from PR description.) |
| Complexity Guard | ✅ Passed | No additional explanation was reported. |
| Bundle Size Monitor | ✅ Passed | No additional explanation was reported. |
This comment is updated automatically whenever Autter reviews a new PR revision.
| def test_nested_values_and_usage_counters(self): | ||
| out = redact_attributes( | ||
| { | ||
| "db": {"dsn": "postgres://u:p@h/db", "args": ["redis://:pw@cache:6379"]}, |
There was a problem hiding this comment.
🔴 [deterministic] Hardcoded secret — Risk: 100/100
A Postgres connection URL with credentials appears to be hard-coded in source. Rotate the value and move it to a secret manager / environment variable. Blast radius — if this is exploited it cascades to the downstream usage that depends on this file: dependent files packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/index.ts, packages/otlp-ingester/src/auth.ts, packages/otlp-ingester/src/sink.ts, packages/otlp-ingester/src/normalize-browser.test.ts, packages/otlp-ingester/src/normalize.test.ts, packages/otlp-ingester/src/server.sink.test.ts, packages/otlp-ingester/src/sink.test.ts.
References:
- https://docs.github.com/code-security/secret-scanning/about-secret-scanning — # Secret scanning Prevent fraudulent use of your secrets by automatically detecting exposed credentials before they can be exploited. ## In this article ## Up next Push protection
- https://docs.github.com/en/code-security/reference/secret-security/supported-secret-scanning-patterns — Lists of supported secrets and the partners that GitHub works with to prevent fraudulent use of secrets that were committed accidentally.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/normalize-browser.test.ts,packages/otlp-ingester/src/normalize.test.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `adapters/python/test_redact.py` around line 52, what looks like a real credential is committed in source. Move it to a secret manager / env var, rotate the leaked value, and add the pattern to `.gitleaks`/`detect-secrets` so it cannot land again. If this is genuinely test data, prefix the value with `EXAMPLE_` / `YOUR_KEY` or mock the credential. Blast radius — if this is exploited it cascades to the downstream usage that depends on this file: dependent files `packages/otlp-ingester/src/normalize-otlp.ts`, `packages/otlp-ingester/src/index.ts`, `packages/otlp-ingester/src/auth.ts`, `packages/otlp-ingester/src/sink.ts`, `packages/otlp-ingester/src/normalize-browser.test.ts`, `packages/otlp-ingester/src/normalize.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`, `packages/otlp-ingester/src/sink.test.ts`.
Flagged by Autter security & observability checks.
| "text": [ | ||
| { | ||
| "name": "JWT inside an error message", | ||
| "input": "invalid session eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c for user", |
There was a problem hiding this comment.
🔴 [deterministic] Hardcoded secret — Risk: 100/100
A JWT-shaped value appears to be hard-coded in source. Rotate the value and move it to a secret manager / environment variable. Blast radius — if this is exploited it cascades to the downstream usage that depends on this file: dependent files packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/index.ts, packages/otlp-ingester/src/auth.ts, packages/otlp-ingester/src/sink.ts, packages/otlp-ingester/src/normalize-browser.test.ts, packages/otlp-ingester/src/normalize.test.ts, packages/otlp-ingester/src/server.sink.test.ts, packages/otlp-ingester/src/sink.test.ts.
References:
- https://docs.github.com/code-security/secret-scanning/about-secret-scanning — # Secret scanning Prevent fraudulent use of your secrets by automatically detecting exposed credentials before they can be exploited. ## In this article ## Up next Push protection
- https://docs.github.com/en/code-security/reference/secret-security/supported-secret-scanning-patterns — Lists of supported secrets and the partners that GitHub works with to prevent fraudulent use of secrets that were committed accidentally.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/normalize-browser.test.ts,packages/otlp-ingester/src/normalize.test.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `test-vectors/redaction.json` around line 6, what looks like a real credential is committed in source. Move it to a secret manager / env var, rotate the leaked value, and add the pattern to `.gitleaks`/`detect-secrets` so it cannot land again. If this is genuinely test data, prefix the value with `EXAMPLE_` / `YOUR_KEY` or mock the credential. Blast radius — if this is exploited it cascades to the downstream usage that depends on this file: dependent files `packages/otlp-ingester/src/normalize-otlp.ts`, `packages/otlp-ingester/src/index.ts`, `packages/otlp-ingester/src/auth.ts`, `packages/otlp-ingester/src/sink.ts`, `packages/otlp-ingester/src/normalize-browser.test.ts`, `packages/otlp-ingester/src/normalize.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`, `packages/otlp-ingester/src/sink.test.ts`.
Flagged by Autter security & observability checks.
| // A process span whose thrown error carries a secret. | ||
| await autter | ||
| .withProcessSpan("nightly-sync", () => { | ||
| throw new Error("redis://:redispass99@cache:6379 refused"); |
There was a problem hiding this comment.
🔴 [deterministic] Database connection URL with embedded credentials — Risk: 90/100
A Redis connection URL with credentials appears to be hard-coded in source. Treat the credential as compromised and rotate it now. Blast radius — if this credential is exploited it cascades to the downstream usage that depends on this file: dependent files packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/index.ts, packages/otlp-ingester/src/auth.ts, packages/otlp-ingester/src/sink.ts, packages/otlp-ingester/src/normalize-browser.test.ts, packages/otlp-ingester/src/normalize.test.ts, packages/otlp-ingester/src/server.sink.test.ts, packages/otlp-ingester/src/sink.test.ts.
References:
- https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html — Cloud-native secret managers often provide built-in support for automated rotation using serverless functions (e.g., AWS Lambda, Azure Functions).
- https://docs.aws.amazon.com/pdfs/wellarchitected/latest/security-pillar/wellarchitected-security-pillar.pdf — For access to databases and third-party tools, we recommend storing access credentials in AWS Secrets Manager and granting access to the ...
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/normalize-browser.test.ts,packages/otlp-ingester/src/normalize.test.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `packages/runtime-node/test/fixtures/e2e-redaction.mjs` around line 42 a Redis connection URL with credentials is hard-coded in source. Treat the literal as compromised even if the PR is closed without merging. Replace the embedded password with an env-var reference (`postgres://app:${PG_PASSWORD}@host/db`) and rotate the database user password on the database server. Move the value to your project's secret manager (AWS Secrets Manager / GCP Secret Manager / Azure Key Vault / HashiCorp Vault / Doppler / 1Password) and load it at runtime via an env var. Diff-only: rotation + amending this PR (drop the literal) is sufficient. Affected services / downstream impact Blast radius — if this credential is exploited it cascades to the downstream usage that depends on this file: dependent files `packages/otlp-ingester/src/normalize-otlp.ts`, `packages/otlp-ingester/src/index.ts`, `packages/otlp-ingester/src/auth.ts`, `packages/otlp-ingester/src/sink.ts`, `packages/otlp-ingester/src/normalize-browser.test.ts`, `packages/otlp-ingester/src/normalize.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`, `packages/otlp-ingester/src/sink.test.ts`..
Flagged by Autter security & observability checks.
| await autter | ||
| .withLlmCall({ provider: "openai", model: "gpt-x" }, (llm) => { | ||
| llm.setAttributes({ "gen_ai.request.api_key": "should-not-leak", note: "ok" }); | ||
| throw new Error("401 Incorrect API key provided: sk-proj-abcdefghijklmnopqrstuvwxyz0123"); |
There was a problem hiding this comment.
🔴 [deterministic] API key in source — Risk: 90/100
A generic api key appears to be hard-coded in source. Treat the credential as compromised and rotate it now. Blast radius — if this credential is exploited it cascades to the downstream usage that depends on this file: dependent files packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/index.ts, packages/otlp-ingester/src/auth.ts, packages/otlp-ingester/src/sink.ts, packages/otlp-ingester/src/normalize-browser.test.ts, packages/otlp-ingester/src/normalize.test.ts, packages/otlp-ingester/src/server.sink.test.ts, packages/otlp-ingester/src/sink.test.ts.
References:
- https://docs.github.com/code-security/secret-scanning/about-secret-scanning — Secret scanning scans your entire Git history on all branches of your repository for hardcoded credentials, including API keys, passwords, tokens, and other ...
- https://docs.github.com/en/code-security/tutorials/remediate-leaked-secrets/remediating-a-leaked-secret — Secrets, such as API keys, tokens and credentials, can pose significant security risks to your team and organization if inadvertently exposed in your codebase
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/normalize-browser.test.ts,packages/otlp-ingester/src/normalize.test.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `packages/runtime-node/test/fixtures/e2e-redaction.mjs` around line 50 a generic api key is hard-coded in source. Treat the literal as compromised even if the PR is closed without merging. Revoke the leaked token on the provider console, mint a replacement scoped to the minimum permissions you need, and audit recent provider-side activity. Move the value to your project's secret manager (AWS Secrets Manager / GCP Secret Manager / Azure Key Vault / HashiCorp Vault / Doppler / 1Password) and load it at runtime via an env var. Diff-only: rotation + amending this PR (drop the literal) is sufficient. Affected services / downstream impact Blast radius — if this credential is exploited it cascades to the downstream usage that depends on this file: dependent files `packages/otlp-ingester/src/normalize-otlp.ts`, `packages/otlp-ingester/src/index.ts`, `packages/otlp-ingester/src/auth.ts`, `packages/otlp-ingester/src/sink.ts`, `packages/otlp-ingester/src/normalize-browser.test.ts`, `packages/otlp-ingester/src/normalize.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`, `packages/otlp-ingester/src/sink.test.ts`..
Flagged by Autter security & observability checks.
| request: { headers: { authorization: "Bearer abcdefghijklmnop123" }, url: "/cb?access_token=tok123456" }, | ||
| }); | ||
| captureMessage("payment for ORD-12345 used token eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.SflKxwRJSMeKKF2QT4fwpM"); | ||
| listeners.get("unhandledrejection")({ reason: new Error("redis://:cachepw@cache:6379 down") }); |
There was a problem hiding this comment.
🟠 [deterministic] Database connection URL with embedded credentials — Risk: 69/100
A Redis connection URL with credentials appears to be hard-coded in source. Treat the credential as compromised and rotate it now. Blast radius — if this credential is exploited it cascades to the downstream usage that depends on this file: dependent files packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/index.ts, packages/otlp-ingester/src/auth.ts, packages/otlp-ingester/src/sink.ts, packages/otlp-ingester/src/normalize-browser.test.ts, packages/otlp-ingester/src/normalize.test.ts, packages/otlp-ingester/src/server.sink.test.ts, packages/otlp-ingester/src/sink.test.ts.
References:
- https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html — Cloud-native secret managers often provide built-in support for automated rotation using serverless functions (e.g., AWS Lambda, Azure Functions).
- https://docs.aws.amazon.com/pdfs/wellarchitected/latest/security-pillar/wellarchitected-security-pillar.pdf — For access to databases and third-party tools, we recommend storing access credentials in AWS Secrets Manager and granting access to the ...
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/normalize-browser.test.ts,packages/otlp-ingester/src/normalize.test.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `packages/runtime-browser/test/redact-e2e.test.mjs` around line 35 a Redis connection URL with credentials is hard-coded in source. Treat the literal as compromised even if the PR is closed without merging. Replace the embedded password with an env-var reference (`postgres://app:${PG_PASSWORD}@host/db`) and rotate the database user password on the database server. Move the value to your project's secret manager (AWS Secrets Manager / GCP Secret Manager / Azure Key Vault / HashiCorp Vault / Doppler / 1Password) and load it at runtime via an env var. Diff-only: rotation + amending this PR (drop the literal) is sufficient. Affected services / downstream impact Blast radius — if this credential is exploited it cascades to the downstream usage that depends on this file: dependent files `packages/otlp-ingester/src/normalize-otlp.ts`, `packages/otlp-ingester/src/index.ts`, `packages/otlp-ingester/src/auth.ts`, `packages/otlp-ingester/src/sink.ts`, `packages/otlp-ingester/src/normalize-browser.test.ts`, `packages/otlp-ingester/src/normalize.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`, `packages/otlp-ingester/src/sink.test.ts`..
Flagged by Autter security & observability checks.
|
|
||
| test("secrets inside nested string values are scrubbed, not just keys", () => { | ||
| const out = redactAttributes({ | ||
| job: { args: ["--db", "postgres://u:pw123@db/x"], note: "Cookie: a=b" }, |
There was a problem hiding this comment.
🟠 [deterministic] Database connection URL with embedded credentials — Risk: 69/100
A Postgres connection URL with credentials appears to be hard-coded in source. Treat the credential as compromised and rotate it now. Blast radius — if this credential is exploited it cascades to the downstream usage that depends on this file: dependent files packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/index.ts, packages/otlp-ingester/src/auth.ts, packages/otlp-ingester/src/sink.ts, packages/otlp-ingester/src/normalize-browser.test.ts, packages/otlp-ingester/src/normalize.test.ts, packages/otlp-ingester/src/server.sink.test.ts, packages/otlp-ingester/src/sink.test.ts.
References:
- https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html — Cloud-native secret managers often provide built-in support for automated rotation using serverless functions (e.g., AWS Lambda, Azure Functions).
- https://docs.aws.amazon.com/pdfs/wellarchitected/latest/security-pillar/wellarchitected-security-pillar.pdf — For access to databases and third-party tools, we recommend storing access credentials in AWS Secrets Manager and granting access to the ...
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/normalize-browser.test.ts,packages/otlp-ingester/src/normalize.test.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `packages/runtime-node/test/redact.test.mjs` around line 318 a Postgres connection URL with credentials is hard-coded in source. Treat the literal as compromised even if the PR is closed without merging. Replace the embedded password with an env-var reference (`postgres://app:${PG_PASSWORD}@host/db`) and rotate the database user password on the database server. Move the value to your project's secret manager (AWS Secrets Manager / GCP Secret Manager / Azure Key Vault / HashiCorp Vault / Doppler / 1Password) and load it at runtime via an env var. Diff-only: rotation + amending this PR (drop the literal) is sufficient. Affected services / downstream impact Blast radius — if this credential is exploited it cascades to the downstream usage that depends on this file: dependent files `packages/otlp-ingester/src/normalize-otlp.ts`, `packages/otlp-ingester/src/index.ts`, `packages/otlp-ingester/src/auth.ts`, `packages/otlp-ingester/src/sink.ts`, `packages/otlp-ingester/src/normalize-browser.test.ts`, `packages/otlp-ingester/src/normalize.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`, `packages/otlp-ingester/src/sink.test.ts`..
Flagged by Autter security & observability checks.
🧭 PR hygiene & process suggestionsAutter has 1 suggestion(s) about the shape of this PR (size, scope, reviewability). These are process guidance — not code defects — so they are consolidated here instead of posted as inline comments on individual files. 🟠 PR changes too many lines — split it — Risk: 73/100This PR changes 1975 lines (limit 1000). Largest contributors: References:
🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter PR-hygiene checks. 💡 7 suggestion(s) — conventions, hardening and hygiene, not defectsInline comments are reserved for concrete defects. These are things worth knowing that the diff does not prove wrong — a missing hardening layer, a convention the repo usually follows, a file that historically changes alongside one you touched. Skim, adopt what fits, ignore the rest.
|
🤖 Release Notes CuratorImpact: minor. This adds new user-facing options and exports ( Changelog entry
Custom agent · runs after review · configured in Autter |
| occurredAt: Date, | ||
| counts: { requestCount?: number; errorCount?: number; sessionCount?: number }, | ||
| ): void { | ||
| route = scrubText(route); |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/style/noParameterAssign — Risk: 55/100
Reassigning a function parameter is confusing.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/style/noParameterAssign` issue at packages/otlp-ingester/src/normalize-browser.ts:111: Reassigning a function parameter is confusing.
Flagged by Autter security & observability checks.
|
|
||
| test("sensitive keys are masked at any depth; safe keys survive", () => { | ||
| for (const key of vectors.keys.sensitive) { | ||
| const out = sanitizeRuntimeContext({ outer: { list: [{ [key]: "raw" }] } }) as any; |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/suspicious/noExplicitAny — Risk: 55/100
Unexpected any. Specify a different type.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/suspicious/noExplicitAny` issue at packages/otlp-ingester/src/redact.test.ts:37: Unexpected any. Specify a different type.
Flagged by Autter security & observability checks.
| before(async () => { | ||
| chStub = createServer((req, res) => { | ||
| let body = ""; | ||
| req.on("data", (chunk) => (body += chunk)); |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/suspicious/noAssignInExpressions — Risk: 55/100
The assignment should not be in an expression.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/suspicious/noAssignInExpressions` issue at packages/otlp-ingester/src/redact.test.ts:74: The assignment should not be in an expression.
Flagged by Autter security & observability checks.
| attributes: [ | ||
| str("exception.type", "OperationalError"), | ||
| str("exception.message", `stripe rejected ${STRIPE} for jane@example.com`), | ||
| str("exception.stacktrace", 'Traceback (most recent call last):\n File "/app/pay.py", line 9, in charge\n stripe.api_key = "' + STRIPE + '"\nOperationalError: password=hunter3py'), |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/style/useTemplate — Risk: 55/100
Template literals are preferred over string concatenation.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/style/useTemplate` issue at packages/otlp-ingester/src/redact.test.ts:163: Template literals are preferred over string concatenation.
Flagged by Autter security & observability checks.
| const digits = value.replace(/\D/g, ""); | ||
| let sum = 0; | ||
| for (let i = 0; i < digits.length; i++) { | ||
| let d = +digits[digits.length - 1 - i]!; |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/style/noNonNullAssertion — Risk: 55/100
Forbidden non-null assertion.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/style/noNonNullAssertion` issue at packages/runtime-browser/src/index.ts:151: Forbidden non-null assertion.
Flagged by Autter security & observability checks.
| if (custom === false) return value; | ||
| let out = String(value) | ||
| .replace(SECRET_RE, MASK) | ||
| .replace(HEADER_RE, "$1" + MASK) |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/style/useTemplate — Risk: 55/100
Template literals are preferred over string concatenation.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/style/useTemplate` issue at packages/runtime-browser/src/index.ts:168: Template literals are preferred over string concatenation.
Flagged by Autter security & observability checks.
|
|
||
| ### Security | ||
|
|
||
| - Scrub secrets and PII from exception messages, stack traces and span status messages, not only custom attributes; re-scrub every span at export so third-party instrumentations, HTTP URLs (`?token=`) and `recordException` calls are covered; scrub `LlmCallHandle.setAttributes`, `instrumentLlmClient` attributes and provider errors, and module-level captures made before `initAutterServer` (`runtime-node`). |
There was a problem hiding this comment.
🟡 [deterministic] markdownlint: MD013 — Risk: 30/100
Line length: Expected: 80; Actual: 407
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the markdownlint `MD013` issue at CHANGELOG.md:7: Line length: Expected: 80; Actual: 407
Flagged by Autter security & observability checks.
| | `CLIENT_RATE_LIMIT_PER_MINUTE` | `120` | Per-key fixed window (client keys) | | ||
| | `OCCURRENCE_TTL_DAYS` / `SPAN_TTL_DAYS` / `METRICS_TTL_DAYS` | `14` / `7` / `90` | ClickHouse TTLs (applied at table creation) | | ||
| | `LLM_CALL_TTL_DAYS` | `90` | Retention for `runtime_llm_calls` rows | | ||
| | `AUTTER_REDACT_VALUE_PATTERNS` | — | JSON array of extra regex sources masked in stored/forwarded text and attribute values (built-in secret/PII patterns always apply) | |
There was a problem hiding this comment.
🟡 [deterministic] markdownlint: MD013 — Risk: 30/100
Line length: Expected: 80; Actual: 171
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the markdownlint `MD013` issue at packages/otlp-ingester/README.md:69: Line length: Expected: 80; Actual: 171
Flagged by Autter security & observability checks.
| const HEADER_VALUE_RE = | ||
| /((?:^|[^\w-])(?:proxy-)?(?:authorization|(?:set-)?cookie)["']?\s*[:=]\s*["']?)[^"'\r\n]+/gim; | ||
| const SECRET_ASSIGNMENT_RE = | ||
| /(password|passwd|pwd|passphrase|secret|token|api[_-]?key|apikey|access[_-]?key|private[_-]?key|credential|signature|session[_-]?id|sessionid|ssn)(["']?\s*[:=]\s*)("[^"\r\n]*"|'[^'\r\n]*'|[^\s"'&,;)}\]\[<>]+)/gi; |
There was a problem hiding this comment.
🟡 [deterministic] oxlint: eslint(no-useless-escape) — Risk: 30/100
packages/otlp-ingester/src/redact.ts:29:203: Unnecessary escape character '['
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the oxlint `eslint(no-useless-escape)` issue at packages/otlp-ingester/src/redact.ts:29: packages/otlp-ingester/src/redact.ts:29:203: Unnecessary escape character '['
Flagged by Autter security & observability checks.
| | `setContext(ctx)` | Attached to subsequent events | | ||
| | `flush()` | Force-send the queue (also runs on page hide/unload) | | ||
| | `redactContext(ctx)` | Mask obvious PII in a context bag (applied to every event automatically) | | ||
| | `redactContext(ctx)` | Mask secrets/PII in a context bag, nested (applied to every event automatically) | |
There was a problem hiding this comment.
🟡 [deterministic] markdownlint: MD013 — Risk: 30/100
Line length: Expected: 80; Actual: 107
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the markdownlint `MD013` issue at packages/runtime-browser/README.md:85: Line length: Expected: 80; Actual: 107
Flagged by Autter security & observability checks.
| const HEADER_RE = | ||
| /((^|[^\w-])(proxy-)?(authorization|(set-)?cookie)["']?\s*[:=]\s*["']?)[^"'\r\n]+/gim; | ||
| const ASSIGN_RE = | ||
| /(password|passwd|pwd|passphrase|secret|token|api[_-]?key|apikey|access[_-]?key|private[_-]?key|credential|signature|session[_-]?id|sessionid|ssn)(["']?\s*[:=]\s*)("[^"\r\n]*"|'[^'\r\n]*'|[^\s"'&,;)}\]\[<>]+)/gi; |
There was a problem hiding this comment.
🟡 [deterministic] oxlint: eslint(no-useless-escape) — Risk: 30/100
packages/runtime-browser/src/index.ts:141:203: Unnecessary escape character '['
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the oxlint `eslint(no-useless-escape)` issue at packages/runtime-browser/src/index.ts:141: packages/runtime-browser/src/index.ts:141:203: Unnecessary escape character '['
Flagged by Autter security & observability checks.
| * by `=` or `:`. Value ends at a delimiter so the surrounding query string | ||
| * or JSON stays readable. */ | ||
| const SECRET_ASSIGNMENT_RE = | ||
| /(password|passwd|pwd|passphrase|secret|token|api[_-]?key|apikey|access[_-]?key|private[_-]?key|credential|signature|session[_-]?id|sessionid|ssn)(["']?\s*[:=]\s*)("[^"\r\n]*"|'[^'\r\n]*'|[^\s"'&,;)}\]\[<>]+)/gi; |
There was a problem hiding this comment.
🟡 [deterministic] oxlint: eslint(no-useless-escape) — Risk: 30/100
packages/runtime-node/src/redact.ts:106:203: Unnecessary escape character '['
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the oxlint `eslint(no-useless-escape)` issue at packages/runtime-node/src/redact.ts:106: packages/runtime-node/src/redact.ts:106:203: Unnecessary escape character '['
Flagged by Autter security & observability checks.
| if (options === false) { | ||
| return (attributes) => ({ ...(attributes ?? {}) }); | ||
| const passthrough = ((attributes?: Attributes | null) => ({ | ||
| ...(attributes ?? {}), |
There was a problem hiding this comment.
🟡 [deterministic] oxlint: unicorn(no-useless-fallback-in-spread) — Risk: 30/100
packages/runtime-node/src/redact.ts:499:4: Empty fallbacks in spreads are unnecessary
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the oxlint `unicorn(no-useless-fallback-in-spread)` issue at packages/runtime-node/src/redact.ts:499: packages/runtime-node/src/redact.ts:499:4: Empty fallbacks in spreads are unnecessary
Flagged by Autter security & observability checks.
| // Customer-supplied patterns extend the built-ins. | ||
| redact: { keys: /^internal_ref$/, values: [/ORD-\d{5}/g] }, | ||
| }); | ||
| setContext({ checkout: { cardNumber: "4111111111111111", step: "pay" } }); |
There was a problem hiding this comment.
🟠 [deterministic] Possible PII in source: CREDIT_CARD — Risk: 72/100
Possible CREDIT_CARD on this line. Committing real personal data (or leaking it in code/config/logs) is a privacy risk.
🛠 AI fix prompt (copy & paste into your coding agent)
Remove the CREDIT_CARD value at packages/runtime-browser/test/redact-e2e.test.mjs:26. If it's test data, use an obviously-fake placeholder; if it's real, purge it and rotate anything exposed.
Flagged by Autter security & observability checks.
| // Secrets inside exception messages, stacks, and status messages. | ||
| for (const secret of [ | ||
| "hunter2pg", | ||
| "4111 1111 1111 1111", |
There was a problem hiding this comment.
🟠 [deterministic] Possible PII in source: CREDIT_CARD — Risk: 72/100
Possible CREDIT_CARD on this line. Committing real personal data (or leaking it in code/config/logs) is a privacy risk.
🛠 AI fix prompt (copy & paste into your coding agent)
Remove the CREDIT_CARD value at packages/runtime-node/test/autoflush.test.mjs:98. If it's test data, use an obviously-fake placeholder; if it's real, purge it and rotate anything exposed.
Flagged by Autter security & observability checks.
| autter.captureException( | ||
| new Error("connect ECONNREFUSED postgres://admin:hunter2pg@db.internal:5432/app"), | ||
| ); | ||
| autter.captureMessage(`charge failed for card 4111 1111 1111 1111 key ${STRIPE}`, "warning"); |
There was a problem hiding this comment.
🟠 [deterministic] Possible PII in source: CREDIT_CARD — Risk: 72/100
Possible CREDIT_CARD on this line. Committing real personal data (or leaking it in code/config/logs) is a privacy risk.
🛠 AI fix prompt (copy & paste into your coding agent)
Remove the CREDIT_CARD value at packages/runtime-node/test/fixtures/e2e-redaction.mjs:36. If it's test data, use an obviously-fake placeholder; if it's real, purge it and rotate anything exposed.
Flagged by Autter security & observability checks.
| }, | ||
| { | ||
| "name": "credit card numbers (Luhn-valid), spaced and plain", | ||
| "input": "charge 4111 1111 1111 1111 declined; amex 378282246310005", |
There was a problem hiding this comment.
🟠 [deterministic] Possible PII in source: CREDIT_CARD — Risk: 72/100
Possible CREDIT_CARD on this line. Committing real personal data (or leaking it in code/config/logs) is a privacy risk.
🛠 AI fix prompt (copy & paste into your coding agent)
Remove the CREDIT_CARD value at test-vectors/redaction.json:138. If it's test data, use an obviously-fake placeholder; if it's real, purge it and rotate anything exposed.
Flagged by Autter security & observability checks.
|
🟡 [deterministic] Build failed: @autter/runtime-next — Risk: 40/100
What happened: The build failed because TypeScript cannot find the module '@autter/runtime-node' in src/server.ts. How to fix: This looks like an incomplete install or missing lockfile. Ensure '@autter/runtime-node' is listed as a dependency in your package.json and run npm install to resolve the missing module. Build output🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
|
🟠 [ai] Global custom key regexes intermittently skip sensitive keys — Risk: 50/100
🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
| endTime: span.endTime, | ||
| status, | ||
| attributes: redactor.exported(span.attributes), | ||
| links: span.links, |
There was a problem hiding this comment.
🟠 [ai] [P1] Redact link and resource attributes before export — Risk: 60/100
The exporter wrapper is installed around the OTLP trace exporter, but redactSpan scrubs span attributes and event attributes while passing span.links and span.resource through unchanged. OpenTelemetry link attributes and resource attributes are serialized with the span, so secrets in either field bypass this last-line redaction and can leave the process from spans produced by third-party instrumentation. Scrub these nested attribute bags before forwarding them.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/runtime-node/src/server.ts,packages/runtime-node/src/redact-exporter.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Flagged by Autter security & observability checks.
| for (const key in context) { | ||
| const value = context[key]; | ||
| out[key] = SENSITIVE_KEY_RE.test(key) | ||
| out[key] = SENSITIVE_KEY_RE.test(key) || (custom && custom.keys && custom.keys.test(key)) |
There was a problem hiding this comment.
🟠 [ai] [P1] Reset custom key regex state for each context key — Risk: 50/100
redact.keys accepts any RegExp, but this .test() call does not reset lastIndex. With a valid global/sticky key pattern, consecutive matching keys can alternate between matching and failing; the later sensitive value is then copied through scrubValue and queued for transmission unmasked. Reset the regex state for every key (or compile a non-global copy) so the browser capture path consistently honors configured key redaction.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/runtime-browser/src/index.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Flagged by Autter security & observability checks.
| for (const span of spans) { | ||
| try { | ||
| scrubbed.push(redactSpan(span, redactor)); | ||
| } catch { |
There was a problem hiding this comment.
🟡 [ai] Redaction failures silently discard spans as successful exports — Risk: 40/100
If redactSpan() throws for any span, this catch silently omits that span and continues; line 71 then exports only the shortened batch using the original result callback. When the remaining batch (including an empty batch) succeeds, the batch processor treats the export as successful and drops the original spans, so a redaction failure permanently loses telemetry without surfacing an export error or diagnostic. Preserve the fail-closed privacy behavior, but make the dropped-span failure observable and report the export as failed rather than allowing callers to believe the complete batch was exported.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/runtime-node/src/redact-exporter.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Keep un-scrubbed spans out of the exporter, but surface any redaction failure through the exporter result callback (or an explicit observable error/counter) so the batch is not acknowledged as a successful complete export. Ensure the error path does not leak the span contents.
Flagged by Autter security & observability checks.
|
🧪 Autter test runAutter checked This PR changes 26 source files. It ran the project's test command for the workspace this PR touched, because those suites are the fastest check that the change did not break existing behavior. It wrote 3 temporary tests for changed files that had no matching test, so the new behavior is still checked. It also executed 9 checks from the PR's test plan. Targeted agent checks: Autter ran targeted checks against the files this PR changed — it executed uncovered test-plan items and, where a changed file had no matching test, wrote a temporary test to verify the new behavior. Execution summary: 19 checks executed · 15 passed · 4 failed. Why suites failed
Project test commands
Autter targeted verification16 tests executed · 14 passed · 2 failed. Critical feature verificationCould not run: no valid structured result (exit=0, hadFile=true); Code-level simulationsRevision
Declared tests: 23 test file(s) found — 14 ran, 2 not observed in suite output, 7 did not run. The project's command reported 8 of them. Autter ran the other 6 files one by one so every declared test has a verdict (6 passed, 0 failed). "Did not run" means no test command that Autter executed covers the file, so nothing in it was checked. "Not observed" means a test command ran but never named the file in its output. A runner that stops at the first failure, shards its files, or prints only a summary produces this. Autter cannot confirm these tests ran, so it does not count them as passed. The @autter/otlp-ingester command failed here, so the files after the failure most probably never started.
|
| Changed file | Related test | Result |
|---|---|---|
packages/otlp-ingester/src/redact.ts |
packages/otlp-ingester/src/redact.test.ts |
✅ existing test passes |
adapters/python/caught_exceptions.py |
adapters/python/test_redact.py |
✅ existing test passes |
adapters/python/redact.py |
adapters/python/test_redact.py |
✅ existing test passes |
packages/otlp-ingester/src/server.ts |
packages/otlp-ingester/src/redact.test.ts |
✅ existing test passes |
packages/otlp-ingester/src/config.ts |
packages/otlp-ingester/src/continuous-detection.test.ts |
✅ no declared test — temporary test passed |
packages/otlp-ingester/src/context.ts |
packages/otlp-ingester/src/redact.test.ts |
✅ existing test passes |
packages/otlp-ingester/src/llm.ts |
packages/otlp-ingester/src/redact.test.ts |
✅ no declared test — temporary test passed |
packages/otlp-ingester/src/normalize-browser.ts |
packages/otlp-ingester/src/normalize-browser.test.ts |
✅ no declared test — temporary test passed |
🤖 Coverage-check evidence
packages/otlp-ingester/src/redact.ts
adapters/python/caught_exceptions.py
adapters/python/redact.py
packages/otlp-ingester/src/server.ts
packages/otlp-ingester/src/config.ts
packages/otlp-ingester/src/context.ts
packages/otlp-ingester/src/llm.ts
packages/otlp-ingester/src/normalize-browser.ts
Temporary tests are written under .autter/scratch/ for verification only — they are never committed to the repository.
Test plan (from the PR description)
- ❌ Run
npm run buildand confirm it passes. — agent execution observed the wrong behavior - ❌ Run
npm run test -w @autter/runtime-node,npm run test -w @autter/runtime-browserandnpm run test -w @autter/otlp-ingester. — npm run test in otlp-ingester failed. - ✅ Run
npm run size -w @autter/runtime-browserand confirm the bundle is under 5 KB brotlied. — verified by agent execution - ✅ Run
python3 -m unittest discover -s adapters/python -vand confirm the shared vectors intest-vectors/redaction.jsonpass. — verified by agent execution - ⬜ Start the local stack (Docker Compose, ClickHouse on 8123, ingester on 4318). POST a
/v1/browserversion 1 payload whosemessageandstackcontain a bearer token, a JWT and an email, and whosecontexthas the same secrets under benign keys such asnoteanddebug. Read the row back fromruntime_error_occurrencesand confirm none of the raw values are stored. — No local stack run provided. - ⬜ Send OTLP traces with an exception event, a
gen_ai.*span and aurl.pathcontaining secret-shaped values. Confirmruntime_spans,runtime_llm_callsandruntime_metrics_1mrows hold masked values. — No OTLP trace run provided. - ❌ Start the ingester with
AUTTER_REDACT_VALUE_PATTERNS='["internal-[0-9]{6}"]'and confirm a matching string is masked. Then set it tonot-jsonand to["("], and confirm startup fails with a message naming the variable. — agent execution observed the wrong behavior - ✅ With
AUTTER_SINK_URLpointed at a local listener, confirm the forwarded batch carries scrubbed fields only. — verified by agent execution - ✅ In a Node app using
initAutterServer, callcaptureExceptionwith an error whose message and attributes contain a secret, plus an object with a circular reference. Confirm the OTLP export is masked and the call does not throw. — verified by agent execution - ✅ Exercise
createBrowserRelayHandlerwith a payload containing{context:{note:"sk-…"}}and confirm the forwarded request is masked. — verified by agent execution - ✅ Compare the fingerprint of an error with no secrets before and after this change, and confirm it is unchanged. For one containing a secret, confirm the regroup behaviour is understood. — verified by agent execution
🤖 Agent-executed checks
❌ Run npm run build and confirm it passes.
Browser and Node packages built; ingester tsc failed (TS2307) because express, @clickhouse/client, and zod are unavailable, followed by implicit-any errors.
✅ Run npm run size -w @autter/runtime-browser and confirm the bundle is under 5 KB brotlied.
Size: 4.14 kB with dependencies, minified and brotlied; limit 5 kB.
✅ Run python3 -m unittest discover -s adapters/python -v and confirm the shared vectors in test-vectors/redaction.json pass.
Ran 8 tests in 0.004s — OK; shared text vectors and sensitive/safe key vectors passed.
❌ Start the ingester with AUTTER_REDACT_VALUE_PATTERNS='["internal-[0-9]{6}"]' and confirm a matching string is masked. Then set it to not-json and to ["("], and confirm startup fails with a message naming the variable.
Valid pattern masked internal-123456. Invalid inputs threw, but messages were 'Unexpected token o, "not-json" is not valid JSON' and 'Invalid regular expression: /(/: Unterminated group'; neither nam…
✅ With AUTTER_SINK_URL pointed at a local listener, confirm the forwarded batch carries scrubbed fields only.
Local sink listener received a batch from SinkForwarder; raw bearer token, email, and API-key values were absent and the redaction marker was present.
✅ In a Node app using initAutterServer, call captureException with an error whose message and attributes contain a secret, plus an object with a circular reference. Confirm the OTLP export is masked and the call does not throw.
Local OTLP collector received /v1/traces; captureException did not throw with circular context, all tested raw secrets were absent, and [redacted] appeared in the export.
✅ Exercise createBrowserRelayHandler with a payload containing {context:{note:"sk-…"}} and confirm the forwarded request is masked.
createBrowserRelayHandler returned 202; local forward listener received the request with context masked and raw token absent.
✅ Compare the fingerprint of an error with no secrets before and after this change, and confirm it is unchanged. For one containing a secret, confirm the regroup behaviour is understood.
Secret-free fingerprint was unchanged. Before scrubbing, two secret variants differed; after applying scrubOccurrence, they intentionally grouped to the same fingerprint.
⬜ items could not be verified automatically and still need a manual check.
| @@ -0,0 +1,209 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: @autter/otlp-ingester — Risk: 40/100
npm run test could not verify the @autter/otlp-ingester test suite in packages/otlp-ingester — this is not evidence that this PR breaks it.
What happened: The test runner fails with ERR_MODULE_NOT_FOUND because the 'zod' package cannot be resolved when src/normalize-browser.ts imports it; this looks like an incomplete install or a missing/undeclared dependency in the lockfile rather than a logic bug in the changed code.
How to fix: Run a clean install (e.g. delete node_modules and re-run npm install / the workspace install) so 'zod' is present, and confirm packages/otlp-ingester's package.json declares zod as a dependency with the lockfile updated accordingly; if zod is only needed at build time, check whether it should be a devDependency instead.
Test output
separates distinct signals (0.78155ms)
✔ fingerprint groups per-value message variants into one issue (1.106467ms)
✔ normalizeStackFrames extracts golden frames for go (2.20812ms)
✔ fingerprint is stable across repeated ingestion for go (0.446163ms)
✔ fingerprint survives line-number drift across re-deploys for go (0.399426ms)
✔ different defects with the same message stay separate for go (0.372941ms)
✔ normalizeStackFrames extracts golden frames for rust (0.67163ms)
✔ fingerprint is stable across repeated ingestion for rust (0.427537ms)
✔ fingerprint survives line-number drift across re-deploys for rust (0.522181ms)
✔ different defects with the same message stay separate for rust (0.406966ms)
---
✔ wide event logs preserve nested context, stable IDs and native trace IDs (3.122218ms)
✔ OTLP protobuf logs are decoded with the standard field numbers (7.884131ms)
✔ serialized context and path queries are scrubbed and excess batches fail explicitly (53.209882ms)
node:internal/modules/package_json_reader:331
throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base), null);
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'zod' imported from /tmp/autter-agentic-ySbV…
🛠 AI fix prompt (copy & paste into your coding agent)
The @autter/otlp-ingester test suite (`npm run test` in `packages/otlp-ingester`) fails. The test runner fails with ERR_MODULE_NOT_FOUND because the 'zod' package cannot be resolved when src/normalize-browser.ts imports it; this looks like an incomplete install or a missing/undeclared dependency in the lockfile rather than a logic bug in the changed code. Fix: Run a clean install (e.g. delete node_modules and re-run npm install / the workspace install) so 'zod' is present, and confirm packages/otlp-ingester's package.json declares zod as a dependency with the lockfile updated accordingly; if zod is only needed at build time, check whether it should be a devDependency instead.
Flagged by Autter security & observability checks.
Autter product walkAutter did not drive this change in a browser. This PR changes a backend/API, but Autter could not find a |
|
🟠 [deterministic] Product walk failed: pytest adapters/python/test_redact.py — Risk: 75/100 Planned command The PR rewrites redaction logic across the OTLP ingester, node/browser runtimes, and the Python adapter, and ships a shared test-vector corpus. There is no server start command, so the change is exercised via the package test runners. I target the redaction-specific suites (not the whole repo) and lean on the shared vectors file, which is where boundary/unicode/oversized-payload cases and cross-im Expected: Python adapter redaction tests pass against the same corpus. This is the separate-language port most prone to boundary divergence (unicode handling, regex anchoring) from the TS implementation. 1 attempt(s), 0 of 3 retries used. ERROR: file or directory not found: adapters/python/test_redact.py 🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
Autter review completed — merge blockedAutter completed its review and found at least one confirmed merge-blocking finding. Autter blocked this PR: build; test; product_walk_failure; 1 confirmed correctness/runtime finding(s). See the findings below and the full PR review for details. Other findings explain the additional warnings, but they did not independently block this PR. The merge gate remains blocked until the confirmed finding is resolved. RetryRetrying reruns the review on the same commit. If the commit is unchanged, the same finding and block may be reproduced.
Autter will post a new comment when the retry starts. |
Autter task list
Generated from PR diff, blast radius, and context. Issues found
50 additional non-blocking finding(s) are available in the Autter review. Also detected but not listed above: 55 finding(s) dismissed as likely false positives by verification — see the Autter review dashboard for their verdicts. 🛠 Fix optionsCheck one option and Autter will start a fix run for the unresolved issues above.
Checking a box triggers the fix run immediately — Autter comments back with the issues being fixed and the branch created for each. |
|
Closing as superseded by #26, which contains this PR's commit plus the review fixes, and is now merged. |
Why
Exception data is what reaches LLM-generated fixes, and it routinely carries secrets, for example:
connect ECONNREFUSED postgres://admin:hunter2@…?token=in request URLsUntil now, redaction only covered custom attributes, and in the browser only top-level emails. Everything else went to storage and the sink verbatim: messages, stack traces, span status messages, third-party spans, relayed browser events, and anything sent by older SDKs or plain OTel senders.
This is the SDK and ingester half of the runtime secret-scrubbing work. The backend half, which scrubs right before every LLM prompt, and the narrowing of the agents' org-DB access are in Autter-dev/autter-monorepo#325. Autter-dev/autter-monorepo#327 closes the Ask log-filter oracle.
What changed
SDKs. Scrubbing happens before anything leaves the customer's process, and it is on by default.
captureException/captureMessage, including the pre-init fallbacks;withLlmCall/trackLlmCallerrors;LlmCallHandle.setAttributesandinstrumentLlmClient.RedactingSpanExporter, so every exported span is scrubbed: HTTP URLs,headersToSpanAttributes, third-party instrumentations and app-levelrecordException;redactoption;redactTextexport.redact: { keys, values } | falseoption and ascrubTextexport. The bundle grows from 3.43 kB to 4.14 kB brotlied, within the 5 kB budget.redact.py, used by the caught-exception sampler to scrub tracebacks.Patterns
Authorization/Cookie/Set-Cookieheader text;@in the password;password=/?token=/?api_key=assignments;*authorizationheader,session/sessionid/sid,pwd,dsn, and a boundedssnmatch.Ingester. This is the second line of defence for old SDKs and third-party OTLP senders, before ClickHouse and before the sink that feeds LLM fixing.
AUTTER_REDACT_VALUE_PATTERNS/AUTTER_REDACT_KEY_PATTERNS.Parity:
test-vectors/redaction.jsonruns against node, browser, the ingester and Python. CI now also runs the Python adapter tests.Verification
All CI steps pass locally:
npm run buildsizeunittest🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary
Summary generated by Autter.
Why this change
Secrets (tokens, keys, credentials, emails) could still reach telemetry storage and the sink through error messages, stack traces, and other free-text or attribute fields, including via the browser relay and ingester paths. This PR adds value-level scrubbing across every SDK and ingester outbound field instead of relying only on key-name matching.
What changes for users
Browser, Node, relay and ingester code now scrub secret-shaped values from messages, stacks, routes, context, LLM attributes and span data before they are sent, stored or forwarded. Operators can add custom scrub patterns via
AUTTER_REDACT_VALUE_PATTERNSandAUTTER_REDACT_KEY_PATTERNS, and a Python adapter plus shared test vectors keep implementations consistent.Implementation and review notes
redact.ts(scrubText,scrubOccurrence,configureRedaction,isSensitiveKey), wired intonormalize-browser.ts,normalize-otlp.ts,llm.ts,context.ts, andserver.ts(scrubbing before fingerprinting). Review whether scrubbing before fingerprinting changes existing issue grouping, which the team rules treat as breaking.config.tsaddspatternListEnv, which parses the JSON pattern env vars and throws a plainnew Erroron invalid input at startup. This is deliberate fail-loud behaviour, but it matches a past review learning about untyped errors in this file.redact.tsis substantially reworked, and a newredact-exporter.ts(RedactingSpanExporter) is wired inserver.tsso span data is scrubbed at export.relay.tsandsanitizeBrowserPayloadalso apply scrubbing, addressing earlier findings about secrets under benign context keys.runtime-browser/src/index.ts) gains client-side scrubbing plus new options. Check this against the zero-dependency rule and the 5 KB brotli size budget enforced bynpm run size.redact.test.ts, e2e redaction tests, cross-language vectors intest-vectors/redaction.json, and Python tests run in CI. The fixtures contain credential-shaped literals that secret scanners flagged earlier, so confirm they are clearly fake.{1,64}@...{1,253}), but verify all patterns in the new modules are linear-time and tolerate hostile inputs such as throwing proxies, per earlier ReDoS and trap findings.Changes
Ingester (
@autter/otlp-ingester)src/redact.tsas the single scrubbing module (scrubText,scrubOccurrence,configureRedaction,isSensitiveKey), with unit tests insrc/redact.test.ts.server.ts: callsconfigureRedaction(config)increateIngesterAppand scrubs occurrences before fingerprinting.normalize-browser.ts: scrubs the route inbumpRollup, and the free-formcontextbag goes through the shared sanitiser.normalize-otlp.tsandllm.ts: scrub OTLP free text and LLM attribute subsets viascrubText/isSensitiveKey.context.ts:sanitizeRuntimeContextnow reuses the shared redaction logic instead of its own copy (net -13 lines).config.ts: addsredactValuePatterns/redactKeyPatternstoIngesterConfig, loaded fromAUTTER_REDACT_VALUE_PATTERNS/AUTTER_REDACT_KEY_PATTERNSthrough a newpatternListEnvhelper. The helper validates each entry withnew RegExpand throws at startup on invalid input.Node SDK (
@autter/runtime-node,@autter/runtime-next)redact.ts: reworked value-level scrubbing with an expandedRedactOptions.redact-exporter.ts(new):RedactingSpanExporterscrubs span data at export time, wired intoinitAutterServerinserver.ts, with newAutterServerOptionsfields.relay.ts:sanitizeBrowserPayloadand the relay handlers scrub message, stack, route and nestedcontextvalues, including secrets under benign keys, before forwarding to the ingester.RelayOptionsgains new options.llm-instrument.ts: small adjustments so LLM span attributes pass through the redactor.index.ts(runtime-node) andserver.ts(runtime-next) export the new redaction surface.Browser SDK (
@autter/runtime-browser)src/index.ts: client-side scrubbing of outbound event fields, with newAutterBrowserOptionsfields. The README is updated.Cross-language and tooling
adapters/python/redact.pyandtest_redact.py(new), pluscaught_exceptions.pyupdated to use the redactor.test-vectors/redaction.json: shared vectors consumed by the Python tests andruntime-browser/test/redact-vectors.test.mjs..github/workflows/ci.yml: addspython3 -m unittest discover -s adapters/python -v.runtime-node/test/redact.test.mjs,autoflush.test.mjs,fixtures/e2e-redaction.mjs,runtime-browser/test/redact-e2e.test.mjs.CHANGELOG.md,docs/ARCHITECTURE.md,docs/CONTINUOUS-DETECTION.md, and the three package READMEs.Breaking changes / compatibility notes
server.ts. Messages, stacks or routes that previously contained secret-shaped values will hash differently, so some existing issues may regroup. The team rules treat fingerprint changes as breaking.AUTTER_REDACT_VALUE_PATTERNS/AUTTER_REDACT_KEY_PATTERNS(bad JSON, non-string entries, or invalid regex) now fail ingester startup. Unset variables behave as before./v1/browserversion 1 payload and OTLP surface keep the same shape. Only field values are masked, and the new option fields are optional.Acceptance Criteria
context, LLM attributes and span attributes are masked before leaving the browser SDK, Node SDK or relay, and again before ingester storage or sink forwarding.context.note) in a browser payload sent throughsanitizeBrowserPayloador the relay handlers does not appear in the forwarded request or in ClickHouse.test-vectors/redaction.json.AUTTER_REDACT_VALUE_PATTERNSandAUTTER_REDACT_KEY_PATTERNSextend the built-in patterns, and invalid values fail startup with a clear message naming the variable.npm run build, the three package test suites,npm run size -w @autter/runtime-browserand the Python unittests pass in CI.Test Plan
npm run buildand confirm it passes.npm run test -w @autter/runtime-node,npm run test -w @autter/runtime-browserandnpm run test -w @autter/otlp-ingester.npm run size -w @autter/runtime-browserand confirm the bundle is under 5 KB brotlied.python3 -m unittest discover -s adapters/python -vand confirm the shared vectors intest-vectors/redaction.jsonpass./v1/browserversion 1 payload whosemessageandstackcontain a bearer token, a JWT and an email, and whosecontexthas the same secrets under benign keys such asnoteanddebug. Read the row back fromruntime_error_occurrencesand confirm none of the raw values are stored.gen_ai.*span and aurl.pathcontaining secret-shaped values. Confirmruntime_spans,runtime_llm_callsandruntime_metrics_1mrows hold masked values.AUTTER_REDACT_VALUE_PATTERNS='["internal-[0-9]{6}"]'and confirm a matching string is masked. Then set it tonot-jsonand to["("], and confirm startup fails with a message naming the variable.AUTTER_SINK_URLpointed at a local listener, confirm the forwarded batch carries scrubbed fields only.initAutterServer, callcaptureExceptionwith an error whose message and attributes contain a secret, plus an object with a circular reference. Confirm the OTLP export is masked and the call does not throw.createBrowserRelayHandlerwith a payload containing{context:{note:"sk-…"}}and confirm the forwarded request is masked.Rollback Plan
main. Re-run CI and republish the affected packages (@autter/runtime-node,@autter/runtime-browser,@autter/runtime-next,@autter/otlp-ingester) at a patch version. Applications can pin to the previous published versions in the meantime.ingesterservice. Do not touch the existing Caddyfile.AUTTER_REDACT_VALUE_PATTERNSandAUTTER_REDACT_KEY_PATTERNSand restart. Built-in scrubbing remains active.Related Issues
No linked issue was identified from the branch name (
fix/sdk-secret-scrubbing), commit context or diff.Written for commit 7d936c6. Summary will update on new commits.