Repository navigation
Scrub secrets in every SDK and the ingester, and check SDK/ingester compatibility automatically - #26
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>
…omotion Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ime-edge Adds the private runtime-core package (coded errors, record building, OTLP logs encoding, redaction) bundled into runtime-node 1.5.0 and the new @autter/runtime-edge 1.0.0. runtime-node gains request summaries (autterRequests, autterFastify, withRuntimeRequest), runtimeContext, inline messages, RuntimeError/defineRuntimeErrors/autterErrorResponse, fork/carrier, AI rollup, waitUntil, enrichers, sinks, a /testing subpath and initAutterLogging. 1.4.0 output is golden-tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Redaction regexes are linear on adversarial strings: email, URL credential, JWT, Slack and Autter-key patterns only start a match at the beginning of a run, query stripping is procedural, and strings are capped before scrubbing (runtime-core, ingester, browser). - runtime-edge queues each record with its own destination and drops records when no key is configured. - /v1/logs promotion lookups are bounded in span, count and time range, newest first, with a per-tenant PROMOTION_LOOKUPS_PER_MINUTE budget. - runtime-browser reads optional error metadata defensively. - The sink keeps statusCode 0; proxy-addr 2.0.8 (CVE-2026-90711). - Test scripts build the workspace packages they import. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Users had to match SDK, ingester and ClickHouse schema versions by hand, and a mismatch failed silently. For example, operation logging (1.4.0) sent to an older ingester got 404s that the SDK retried quietly. Runtime now checks this itself. - One source of truth: packages/otlp-ingester/src/compat-manifest.json lists each feature's minimum ingester, migrations, route and minimum SDK versions. compat.ts holds the shared, dependency-free evaluation, which the Node SDK bundles at build time. - Ingester: GET /v1/compat (public, no tenant data) returns the version, schema state and level, and per-feature availability, plus evaluated issues for ?features=&sdk=. Every response carries x-autter-ingester-version (CORS-exposed on /v1/browser). Migration 0012-runtime-compat records the SDK name and version per service in runtime_sdk_versions (deduplicated hourly, fire-and-forget) and the ingester's own version in runtime_ingester_info. - runtime-node / runtime-next: report themselves as telemetry.distro.*. When a feature that needs a newer ingester is in use, check once in the background and warn once per feature, naming both versions and the fix. The check never throws and never delays startup or exit (unref'd socket). It respects compatCheck: false, AUTTER_COMPAT_CHECK=0 and debug mode. Pre-compat ingesters are detected by a 404 on the feature's route. The browser relay checks CSP and network events against the ingester's version header. - npx @autter/runtime-node doctor: a one-shot report. Exit 0 compatible, 1 mismatch or rejected key, 2 unreachable. - runtime-browser sends its version as `sdk` (+10 bytes, 4.15 kB brotli). - Python adapter: stdlib compat.py with warn_if_incompatible and a `doctor` command, and a parity test against the manifest. - docs/COMPATIBILITY.md plus pointers from the docs that described manual version matching. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🚦 Pre-merge checks ·
|
| Check | Status | Explanation |
|---|---|---|
| Too many files changed | This PR changes 70 files (limit 50). Consider splitting this PR by area: packages (53 files), docs (7 files), adapters (5 files), (root) (3 files), test-vectors (1 file), .github (1 file). | |
| Too many lines changed | This PR changes 5501 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 91/100): CHANGELOG.md:26, packages/otlp-ingester/README.md:85, test-vectors/redaction.json:107. | |
| Missing security-team review on sensitive path | 1 root-cause issue(s) detected (max risk 80/100): packages/otlp-ingester/src/server.ts:71. | |
| Missing input validation | 1 potential issue(s) detected (max risk 94/100): packages/otlp-ingester/src/server.ts:195. | |
| API key in source | 1 potential secret(s) detected (max risk 98/100): packages/runtime-node/test/fixtures/e2e-redaction.mjs:50. | |
| Private key in source | 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. | |
| Runtime error risk | 1 potential issue(s) detected (max risk 33/100): adapters/python/redact.py:181. | |
| Maintainability issue | 3 potential issue(s) detected (max risk 49/100): adapters/python/compat.py:309, adapters/python/compat.py:360, adapters/python/test_compat.py:21. | |
| Co-change coupling | 2 file(s) changed without a partner they usually change with. | |
| Release Notes Curator | 1 root-cause issue(s) detected (max risk 40/100): packages/otlp-ingester/src/server.ts:114. |
✅ Passed checks (197)
| Check | Status | Explanation |
|---|---|---|
| Too many unrelated chapters | ✅ Passed | 6 chapter(s) detected, within the limit of 6. |
| Generated files hiding real changes | ✅ Passed | Generated-file volume (3 lines) does not obscure the 5498 hand-written line(s). |
| Missing PR context | ✅ Passed | PR context looks sufficient. |
| Mixed concerns (refactor + behavior change) | ✅ Passed | The PR bundles two separate features, secret redaction and the compat check. Each is a behavior change. The consolidation in normalize-browser.ts, context.ts and llm.ts, which swaps local regexes for the shared scrubText, is part of the redaction behavior change and not a standalone refactor. There is no pure rename or ... [truncated 37 chars] |
| Migration + app logic + UI combined in one PR | ✅ Passed | The PR adds a ClickHouse migration (0012-runtime-compat in migrations.ts and clickhouse.ts) together with ingester and SDK logic. It contains no user-interface changes. The dashboard UI is in a separate autter-monorepo PR, and the browser SDK is telemetry code, not UI. |
| Sensitive data in logs | ✅ Passed | No sensitive data in logs issues detected. |
| 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 remaining after verification. |
| Circuit breaker not detected | ✅ Passed | No circuit breaker not detected issues remaining after verification. |
| Stack trace leakage | ✅ Passed | No stack trace leakage issues detected. |
| Multi-write without detected transaction | ✅ Passed | No multi-write without detected transaction issues remaining after verification. |
| Possible TOCTOU in critical path | ✅ Passed | No possible toctou in critical path issues remaining after verification. |
| 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 remaining after verification. |
| 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 remaining after verification. |
| 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. |
| 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 remaining after verification. |
| 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 remaining after verification. |
| 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. |
| Repetitive boilerplate (duplicated block) | ✅ Passed | No repetitive boilerplate (duplicated block) issues detected. |
| 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 remaining after verification. |
| 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 remaining after verification. |
| 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. |
| 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. |
| Dependency has known CVE | ✅ Passed | No dependency has known cve findings. |
| Malware package in dependency | ✅ Passed | No malware package in dependency findings. |
| Possible typosquat dependency | ✅ Passed | No possible typosquat dependency findings. |
| Newly introduced dependency | ✅ Passed | No newly introduced dependency issues remaining after verification. |
| Unpinned production dependency | ✅ Passed | No unpinned production dependency findings. |
| Deprecated dependency | ✅ Passed | No deprecated dependency findings. |
| License allow-list violation | ✅ Passed | No license allow-list violation findings. |
| Low dependency grade | ✅ Passed | No low dependency grade findings. |
| 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. |
| Resource leak risk | ✅ Passed | No additional explanation was reported. |
| Data integrity risk | ✅ Passed | No additional explanation was reported. |
| 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. |
| Code duplication / DRY violation | ✅ Passed | No additional explanation was reported. |
| AI authorship | ✅ Passed | 60.3% AI-authored (claude:claude-opus-5-5: 60%), 0% human, 40% untracked. (partial coverage — some commits not tracked) |
| Intent vs. implementation | ✅ Passed | The reviewed part of the diff delivers the ingester-side scrubbing and the compatibility tracking the PR describes; the Node, Next, browser and Python SDK work sits in omitted files and could not be checked. Requirements: 2 implemented, 3 partial. 1 change(s) with no backing requirement. (Intent from product context, P ... [truncated 15 chars] |
| 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.
| async insertSdkVersions(ctx: IngestContext, rows: SdkVersionRow[]): Promise<void> { | ||
| if (!rows.length || !this.configured) return; | ||
| await this.ensureSchema(); | ||
| await this.getClient().insert({ |
There was a problem hiding this comment.
🔴 Multi-write without transaction wrapper — Risk: 100/100
Multiple writes (2) in the same change without a transaction wrapper. Blast radius — violating this convention cascades to the downstream usage that depends on this file: functions ClickHouseStore, sanitizeBrowserPayload, extractLlmCall, normalizeTraces, createBrowserRelayFetchHandler, instrumentLlmClient, createBrowserRelayHandler, loadConfig; endpoints POST ${feature.route}, GET /v1/compat; scopes @autter/otlp-ingester; 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/sink.test.ts, packages/otlp-ingester/src/server.sink.test.ts.
References:
- https://www.postgresql.org/docs/current/mvcc.html — This chapter describes the behavior of the PostgreSQL database system when two or more sessions try to access the same data at the same time. The goals in that situation are to all
- https://www.postgresql.org/docs/current/transactions.html — Chapter 67. Transaction Processing Table of Contents 67.1. Transactions and Identifiers 67.2. Transactions and Locking 67.3. Subtransactions 67.4. Two-Phase Transactions This chapt
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Functions/symbols:
ClickHouseStore,sanitizeBrowserPayload,extractLlmCall,normalizeTraces,createBrowserRelayFetchHandler,instrumentLlmClient,createBrowserRelayHandler,loadConfig - API endpoints:
POST ${feature.route},GET /v1/compat - 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/sink.test.ts,packages/otlp-ingester/src/server.sink.test.ts - Scopes:
@autter/otlp-ingester
🛠 AI fix prompt (copy & paste into your coding agent)
In `packages/otlp-ingester/src/clickhouse.ts` around line 250, multiple writes happen in the same hunk without a surrounding transaction (no `transaction(`/`db.tx(`/`withTransaction`/`@transactional`/`with session.begin()`/`BEGIN ... COMMIT`). Wrap them in a transaction so a partial failure cannot leave data inconsistent (e.g. money debited but order not created). Blast radius — violating this convention cascades to the downstream usage that depends on this file: functions `ClickHouseStore`, `sanitizeBrowserPayload`, `extractLlmCall`, `normalizeTraces`, `createBrowserRelayFetchHandler`, `instrumentLlmClient`, `createBrowserRelayHandler`, `loadConfig`; endpoints `POST ${feature.route}`, `GET /v1/compat`; scopes `@autter/otlp-ingester`; 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/sink.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`.
Flagged by Autter security & observability checks.
| { | ||
| "name": "PEM private key block", | ||
| "skip": ["browser"], | ||
| "input": "cfg -----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAabc\n-----END RSA PRIVATE KEY----- loaded", |
There was a problem hiding this comment.
🔴 [deterministic] Private key in source — Risk: 100/100
A private 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: endpoints POST ${feature.route}, GET /v1/compat; 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/sink.test.ts, packages/otlp-ingester/src/server.sink.test.ts.
References:
- https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html — These examples demonstrate how you can create architectures that not only manage secrets securely but also automate the rotation process, significantly reducing ...
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- API endpoints:
POST ${feature.route},GET /v1/compat - 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/sink.test.ts,packages/otlp-ingester/src/server.sink.test.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `test-vectors/redaction.json` around line 103 a private key is hard-coded in source. Treat the literal as compromised even if the PR is closed without merging. Generate a new keypair, distribute the new public key to consumers, and delete the leaked private key. Anything signed by the old key should be re-issued. 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: endpoints `POST ${feature.route}`, `GET /v1/compat`; 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/sink.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`..
Flagged by Autter security & observability checks.
| 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] Database connection URL with embedded credentials — Risk: 96/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: endpoints POST ${feature.route}, GET /v1/compat; 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/sink.test.ts, packages/otlp-ingester/src/server.sink.test.ts.
References:
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/hardcoded-db-creds.html — # Move hardcoded database credentials to AWS Secrets Manager If you have plaintext database credentials in your code, we recommend that you move the credentials to Secrets Manager
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_turn-on-for-db.html — # Set up automatic rotation for Amazon RDS, Amazon Aurora, Amazon Redshift, or Amazon DocumentDB secrets This tutorial describes how to set up Rotation by Lambda function for datab
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- API endpoints:
POST ${feature.route},GET /v1/compat - 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/sink.test.ts,packages/otlp-ingester/src/server.sink.test.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `adapters/python/test_redact.py` around line 52 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: endpoints `POST ${feature.route}`, `GET /v1/compat`; 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/sink.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`..
Flagged by Autter security & observability checks.
| }, | ||
| { | ||
| "name": "mysql URL whose password contains @", | ||
| "input": "mysql://root:p@ss@db:3306/shop", |
There was a problem hiding this comment.
🔴 [deterministic] Database connection URL with embedded credentials — Risk: 96/100
A MySQL 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: endpoints POST ${feature.route}, GET /v1/compat; 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/sink.test.ts, packages/otlp-ingester/src/server.sink.test.ts.
References:
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/hardcoded-db-creds.html — # Move hardcoded database credentials to AWS Secrets Manager If you have plaintext database credentials in your code, we recommend that you move the credentials to Secrets Manager
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_turn-on-for-db.html — # Set up automatic rotation for Amazon RDS, Amazon Aurora, Amazon Redshift, or Amazon DocumentDB secrets This tutorial describes how to set up Rotation by Lambda function for datab
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- API endpoints:
POST ${feature.route},GET /v1/compat - 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/sink.test.ts,packages/otlp-ingester/src/server.sink.test.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `test-vectors/redaction.json` around line 61 a MySQL 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: endpoints `POST ${feature.route}`, `GET /v1/compat`; 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/sink.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`..
Flagged by Autter security & observability checks.
| ...(opts.release ? { release: opts.release } : {}), | ||
| // SDK version: the ingester records it so version mismatches are | ||
| // visible (and checked server-side by the relay). Ignored by older ingesters. | ||
| sdk: SDK_VERSION, |
There was a problem hiding this comment.
🔴 New required field added to request — Risk: 94/100
New required field appears here (TS/JS field). Existing callers that omit it will fail validation. Blast radius — if this contract change ships, the following consumers break: functions AutterBrowserOptions, BrowserEvent, sanitizeBrowserPayload, extractLlmCall, normalizeTraces, createBrowserRelayFetchHandler, instrumentLlmClient, createBrowserRelayHandler; endpoints POST ${feature.route}, GET /v1/compat; 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/sink.test.ts, packages/otlp-ingester/src/server.sink.test.ts.
References:
- https://docs.github.com/en/rest/about-the-rest-api/breaking-changes — The GitHub REST API is versioned. The API version name is based on the date when the API version was released. For example, the API version 2026 ...
- https://docs.stripe.com/api/versioning — You can safely upgrade to any monthly release without updating your code. The current version is 2026-09-30.endive.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Functions/symbols:
AutterBrowserOptions,BrowserEvent,sanitizeBrowserPayload,extractLlmCall,normalizeTraces,createBrowserRelayFetchHandler,instrumentLlmClient,createBrowserRelayHandler - API endpoints:
POST ${feature.route},GET /v1/compat - 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/sink.test.ts,packages/otlp-ingester/src/server.sink.test.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `packages/runtime-browser/src/index.ts` around line 428, this PR adds a new REQUIRED field to a request body / params. Every existing caller that omits it will start failing validation. Either give the field a sensible default, mark it optional, or bump the API version so old clients keep working. Blast radius — if this contract change ships, the following consumers break: functions `AutterBrowserOptions`, `BrowserEvent`, `sanitizeBrowserPayload`, `extractLlmCall`, `normalizeTraces`, `createBrowserRelayFetchHandler`, `instrumentLlmClient`, `createBrowserRelayHandler`; endpoints `POST ${feature.route}`, `GET /v1/compat`; 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/sink.test.ts`, `packages/otlp-ingester/src/server.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: 94/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: endpoints POST ${feature.route}, GET /v1/compat; 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/sink.test.ts, packages/otlp-ingester/src/server.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:
- API endpoints:
POST ${feature.route},GET /v1/compat - 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/sink.test.ts,packages/otlp-ingester/src/server.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: endpoints `POST ${feature.route}`, `GET /v1/compat`; 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/sink.test.ts`, `packages/otlp-ingester/src/server.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: endpoints POST ${feature.route}, GET /v1/compat; 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/sink.test.ts, packages/otlp-ingester/src/server.sink.test.ts.
References:
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/hardcoded-db-creds.html — # Move hardcoded database credentials to AWS Secrets Manager If you have plaintext database credentials in your code, we recommend that you move the credentials to Secrets Manager
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_turn-on-for-db.html — # Set up automatic rotation for Amazon RDS, Amazon Aurora, Amazon Redshift, or Amazon DocumentDB secrets This tutorial describes how to set up Rotation by Lambda function for datab
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- API endpoints:
POST ${feature.route},GET /v1/compat - 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/sink.test.ts,packages/otlp-ingester/src/server.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: endpoints `POST ${feature.route}`, `GET /v1/compat`; 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/sink.test.ts`, `packages/otlp-ingester/src/server.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: 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: endpoints POST ${feature.route}, GET /v1/compat; 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/sink.test.ts, packages/otlp-ingester/src/server.sink.test.ts.
References:
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/hardcoded-db-creds.html — # Move hardcoded database credentials to AWS Secrets Manager If you have plaintext database credentials in your code, we recommend that you move the credentials to Secrets Manager
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_turn-on-for-db.html — # Set up automatic rotation for Amazon RDS, Amazon Aurora, Amazon Redshift, or Amazon DocumentDB secrets This tutorial describes how to set up Rotation by Lambda function for datab
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- API endpoints:
POST ${feature.route},GET /v1/compat - 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/sink.test.ts,packages/otlp-ingester/src/server.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: endpoints `POST ${feature.route}`, `GET /v1/compat`; 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/sink.test.ts`, `packages/otlp-ingester/src/server.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: 69/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: endpoints POST ${feature.route}, GET /v1/compat; 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/sink.test.ts, packages/otlp-ingester/src/server.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:
- API endpoints:
POST ${feature.route},GET /v1/compat - 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/sink.test.ts,packages/otlp-ingester/src/server.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: endpoints `POST ${feature.route}`, `GET /v1/compat`; 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/sink.test.ts`, `packages/otlp-ingester/src/server.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: endpoints POST ${feature.route}, GET /v1/compat; 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/sink.test.ts, packages/otlp-ingester/src/server.sink.test.ts.
References:
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/hardcoded-db-creds.html — # Move hardcoded database credentials to AWS Secrets Manager If you have plaintext database credentials in your code, we recommend that you move the credentials to Secrets Manager
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_turn-on-for-db.html — # Set up automatic rotation for Amazon RDS, Amazon Aurora, Amazon Redshift, or Amazon DocumentDB secrets This tutorial describes how to set up Rotation by Lambda function for datab
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- API endpoints:
POST ${feature.route},GET /v1/compat - 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/sink.test.ts,packages/otlp-ingester/src/server.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: endpoints `POST ${feature.route}`, `GET /v1/compat`; 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/sink.test.ts`, `packages/otlp-ingester/src/server.sink.test.ts`..
Flagged by Autter security & observability checks.
🧭 PR hygiene & process suggestionsAutter has 3 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. 🟡 Requirement only partially built — Risk: 38/100Flagged in 3 places: The PR description asked for: "Python adapter gets a full redactor", but the diff only partially delivers it. CI now runs the Python adapter tests, and the PR lists redact.py and test_redact.py. Those files are in the omitted set, so their content was not checked. 🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter PR-hygiene checks.
|
| for (const feature of COMPAT_MANIFEST.features) { | ||
| assert.ok(!ids.has(feature.id), `duplicate ${feature.id}`); | ||
| ids.add(feature.id); | ||
| assert.ok(compareVersions(feature.ingester, pkgVersion("otlp-ingester"))! <= 0, `${feature.id}: ingester ${feature.ingester} is unreleased`); |
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/otlp-ingester/src/compat.test.ts:36: Forbidden non-null assertion.
Flagged by Autter security & observability checks.
| const right = parseVersion(b); | ||
| if (!left || !right) return null; | ||
| for (let i = 0; i < 4; i++) { | ||
| if (left[i]! !== right[i]!) return left[i]! < right[i]! ? -1 : 1; |
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/otlp-ingester/src/compat.ts:82: Forbidden non-null assertion.
Flagged by Autter security & observability checks.
| const missing = feature.migrations.filter((m) => !applied.has(m)); | ||
| if (schema.status === "failed" || (schema.status === "ready" && missing.length)) { | ||
| const schemaFix = | ||
| "Check the ingester's CLICKHOUSE_URL/credentials and its logs, then restart it so the boot migrations run. " + |
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/compat.ts:239: Template literals are preferred over string concatenation.
Flagged by Autter security & observability checks.
| 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:113: 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 bodies = []; | ||
| const server = createServer((req, res) => { | ||
| let body = ""; | ||
| req.on("data", (c) => (body += c)); |
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/runtime-node/test/relay-compat.test.mjs:14: The assignment should not be in an expression.
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.
| gets one warning: | ||
|
|
||
| ```text | ||
| [autter-runtime] Operation logging needs ingester >= 1.4.0; yours is 1.3.4. Upgrade the ingester: docker pull ghcr.io/autter-dev/otlp-ingester:latest and restart it (ClickHouse migrations run at boot). See https://github.com/Autter-dev/autter-runtime/blob/main/docs/COMPATIBILITY.md |
There was a problem hiding this comment.
🟡 [deterministic] markdownlint: MD013 — Risk: 30/100
Line length: Expected: 80; Actual: 285
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the markdownlint `MD013` issue at docs/COMPATIBILITY.md:15: Line length: Expected: 80; Actual: 285
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.
| 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:143: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:143: packages/runtime-browser/src/index.ts:143: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.
| "@opentelemetry/sdk-trace-base": "^1.30.1", | ||
| "@opentelemetry/semantic-conventions": "^1.28.0" | ||
| }, | ||
| "bin": { |
There was a problem hiding this comment.
🔴 [deterministic] Vulnerable dependency: proxy-addr@2.0.7 — CVE-2026-90711 — Risk: 85/100
proxy-addr@2.0.7 (npm) is affected by CVE-2026-90711 (GHSA-jqcg-44mw-7w3h) — critical severity. proxy-addr vulnerable to IP spoofing via IPv4-mapped IPv6 trust subnet
🛠 AI fix prompt (copy & paste into your coding agent)
Upgrade `proxy-addr` past the version affected by CVE-2026-90711, or apply the advisory's mitigation. Details: https://osv.dev/GHSA-jqcg-44mw-7w3h.
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
1 attempt(s), 0 of 3 retries used. 🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
| const inserts: Record<string, Array<Record<string, unknown>>> = {}; | ||
| const server = 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/server.compat.test.ts:53: The assignment should not be in an expression.
Flagged by Autter security & observability checks.
🧪 Autter test runAutter checked This PR changes 58 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 2 temporary tests for changed files that had no matching test, so the new behavior is still checked. It also executed 2 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: 11 checks executed · 11 passed. Project test commands
Failing tests7 test cases failed.
Autter targeted verification10 tests executed · 10 passed. Critical feature verificationDefense-in-depth redaction of browser error telemetry through the Node relay and ClickHouse persistence. Code-level simulationsRevision
Declared tests: 42 test file(s) found — 26 ran, 0 not observed in suite output, 16 did not run. The project's command reported 13 of them. Autter ran the other 13 files one by one so every declared test has a verdict (6 passed, 7 failed). "Did not run" means no test command that Autter executed covers the file, so nothing in it was checked.
|
| Changed file | Related test | Result |
|---|---|---|
adapters/python/caught_exceptions.py |
adapters/python/test_redact.py |
✅ existing test passes |
adapters/python/compat.py |
adapters/python/test_compat.py |
✅ existing test passes |
adapters/python/redact.py |
adapters/python/test_redact.py |
✅ existing test passes |
packages/otlp-ingester/src/clickhouse.ts |
packages/otlp-ingester/src/server.compat.test.ts |
✅ existing test passes |
packages/otlp-ingester/src/compat.ts |
packages/otlp-ingester/src/compat.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/index.ts |
— | ✅ no declared test — temporary test passed |
🤖 Coverage-check evidence
adapters/python/caught_exceptions.py
adapters/python/compat.py
adapters/python/redact.py
packages/otlp-ingester/src/clickhouse.ts
packages/otlp-ingester/src/compat.ts
packages/otlp-ingester/src/config.ts
packages/otlp-ingester/src/context.ts
packages/otlp-ingester/src/index.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 buildat the repo root and confirmnpm run size -w @autter/runtime-browserstays within budget. — No build or size run provided. - ⬜ Run the ingester tests (
redact.test.ts,compat.test.ts,server.compat.test.ts,coded-errors.test.ts), the runtime-node tests (redact.test.mjs,compat.test.mjs,doctor.test.mjs,relay-compat.test.mjs,autoflush.test.mjs), the runtime-browser tests (redact-vectors,redact-hardening,redact-e2e) andpython -m pytest adapters/python. All must pass. — Only ingester package tests ran; others missing. - ⬜ Start the local ingester with ClickHouse from Docker Compose, then run
curl -i localhost:4318/v1/compat. Check that the ingester-version header is present and that the schema level and migrations look right. — No Docker Compose or curl run. - ⬜ POST a
/v1/browserpayload using the documented curl request, withcontextcontaining{"note":"sk-<fake key>","debug":"AKIA<fake>"}and a fake JWT inmessage. Read the row back from ClickHouse and confirm no secret survives inmessage,stack,routeorattributes. — No POST payload or ClickHouse row check. - ⬜ Send an OTLP trace whose span attributes, event attributes, link attributes and resource attributes contain a fake secret. Confirm the stored span has none, and note whether link and resource attributes are scrubbed by
redact-exporter.ts. — Unit tests ran; no explicit OTLP trace send. - ⬜ Send the same payload through the Node browser relay and confirm the forwarded body is already redacted. — No browser relay payload send.
- ✅ Start the ingester with
AUTTER_REDACT_VALUE_PATTERNS='["internal-[0-9]{6}"]'and confirm a matching value is masked. Then setAUTTER_REDACT_VALUE_PATTERNS='not json'and confirm startup fails with a clear error. — verified by agent execution - ⬜ Run the doctor CLI against the local ingester using a current SDK version, then against an out-of-range version. Check that the output and exit code match
docs/COMPATIBILITY.md. — No doctor CLI run. - ⬜ After a few ingests, query
sdk_versionsandingester_infoin ClickHouse. Confirm the SDK rows are present and thatreportIngesterInfowrites at most once per throttle window. — No ClickHouse query after ingests. - ⬜ Restart the ingester against an already-migrated database to confirm the new migration re-runs without error. Also boot against a fresh database and compare the table shapes. — Migrations applied in tests; restart not confirmed.
- ✅ Check that a fixture error produces the same fingerprint before and after scrubbing for ordinary messages, to rule out unintended issue regrouping. — verified by agent execution
🤖 Agent-executed checks
✅ Start the ingester with AUTTER_REDACT_VALUE_PATTERNS='["internal-[0-9]{6}"]' and confirm a matching value is masked. Then set AUTTER_REDACT_VALUE_PATTERNS='not json' and confirm startup fails with a clear error.
With a local fake ClickHouse HTTP sink, the ingester accepted the browser event (202) and inserted context.note as [redacted]; internal-123456 was absent from the captured row. Invalid JSON exite…
✅ Check that a fixture error produces the same fingerprint before and after scrubbing for ordinary messages, to rule out unintended issue regrouping.
All six ordinary-message stack fixtures (Go, Rust, Java, .NET, Node, Python) produced identical fingerprints before and after scrubbing. The fingerprint test suite also passed 30/30.
⬜ items could not be verified automatically and still need a manual check.
Autter product walkAutter did not drive this change in a browser. This PR changes a backend/API, but Autter could not find a |
Autter review completed — merge blockedAutter completed its review and found at least one confirmed merge-blocking finding. Autter blocked this PR: test. 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. |
runtime-node and runtime-edge bundle the private runtime-core package, and runtime-next imports runtime-node, runtime-edge and runtime-browser for its type declarations. Building any of them alone before its siblings had been built failed with "Could not resolve @autter/runtime-core" or TS2307, which is what the review gate's per-package build hit. scripts/ensure-built.mjs builds a sibling only when its dist/ is missing, so the root build (already in dependency order) does no extra work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
runtime-next imports runtime-node and runtime-browser for its type declarations, so building it alone before them failed with TS2307, which is what the review gate's per-package build hit. scripts/ensure-built.mjs builds a sibling only when its dist/ is missing, so the root build does no extra work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n hostile attributes The per-isolate queue dropped whichever record arrived last once it held maxQueue records or 1 MB, and the summary is always emitted last, so a request logging 250 warnings exported no summary at all. A summary now evicts the oldest plain record, and warn/error records are capped at 50 per request. rt.error and rt.captureException spread the caller's attributes before redaction, so a throwing getter or a Proxy made the user's handler throw. They now copy fields one by one and skip ones that throw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CHANGELOG says cache_creation_tokens counts are no longer masked, but only the ingester was updated; the SDK redactor still turned them (and cache_read_input_tokens, cache_creation_input_tokens, cached_tokens and reasoning_tokens) into "[redacted]". Numeric counts under these keys are kept now; non-numeric values are still masked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
liftErrorFields took the first non-empty autter.error.code across the exception event and its span and validated it afterwards, so an invalid code on the event (ECONNRESET) hid a valid one on the span and the error fell back to message grouping. Same for autter.request.id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ore cutting
- JWT: \beyJ… could start a match at every "eyJ" after a "-", and each
start rescanned the rest of the run. 64 KB of "eyJ-" took 5 s in Node,
3.4 s in the ingester (reachable by any ingest key), 6 s in the browser
and 1.8 s in Python. A lookbehind (a captured prefix in the browser, for
older Safari) lets a match start only at the beginning of a run.
- The ingester caps each scrubbed string at 64 KB and cuts context values to
their stored size before scrubbing; a 900 KB attribute was scrubbed in full
once per exception event.
- Custom key patterns with g/y kept lastIndex between keys, so /internal/g
skipped every other key (Node and browser). Value patterns drop y (it
never matched mid-string) and the browser makes them global, as Node does.
- Browser, relay and the Python sampler cut text before scrubbing, which
left half a secret at the cut ("admin:hunt"). They now scrub with 512
chars of slack, then cut.
- A browser context with a throwing getter or revoked Proxy is dropped
instead of throwing from captureException; a Python value whose __str__
raises is masked.
- The card key rule masked "discard"/"scorecard", contradicting its own
comment; it now needs a non-letter before "card".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The export-time redactor scrubbed span and event attributes but passed links, event names and the resource through. NodeSDK's process detector puts process.command_args on the resource (`--db=postgres://user:pass@…`) and OTEL_RESOURCE_ATTRIBUTES can carry anything, and the resource also rides every metrics export. - Link attributes and event names are scrubbed; the resource is scrubbed once per Resource object (the OTLP serializer groups spans by resource identity), for traces and, via RedactingMetricExporter, metrics. - A batch containing a span that could not be scrubbed still drops that span, but now reports the export as failed instead of a silent success. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Context values have no length limit, so a 1 MB string was scanned in full on the main thread (~0.6 s, and 1.7 s for some shapes). scrubText now scans at most 32 KB; stacks are cut to 32 000 characters after scrubbing anyway. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖 AI authorship60.3% of changed code was AI-written — 0% human, 40% untracked, across 10,577 attributed line(s). By model: Most AI-authored files:
Partial coverage: some commits in this PR were not authored through the autter CLI. Provenance measured at authorship time by the autter CLI. |
🤖 Release Notes CuratorImpact: minor. Every SDK and the ingester now scrub secrets, and a new compatibility check ( Changelog entry
Custom agent · runs after review · configured in Autter |
| name: scrubText(span.name ?? "unnamed").slice(0, 1000), | ||
| kind, | ||
| status: isError ? "error" : "ok", | ||
| route: route ? normalizeRoute(route) : null, |
There was a problem hiding this comment.
🟠 [ai] OTLP route values bypass the new scrubber before persistence — Risk: 55/100
The route is derived from untrusted OTLP attributes and only passed through normalizeRoute, which strips query strings and templates numeric/UUID/hex segments but leaves values such as /users/alice@example.com or a secret embedded in a path unchanged. This value is written directly to each span and reused for request rollups, while the new occurrence scrubber only protects occurrence fields; therefore route PII/secrets still reach ClickHouse despite the ingester-side scrubbing added by this change. Scrub the route before both the span and rollup paths (and retain the existing route normalization).
⚠ 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/fingerprint.ts,packages/otlp-ingester/src/server.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Flagged by Autter security & observability checks.
| // free-text fields old SDKs and third-party OTLP senders pass | ||
| // through verbatim. Attributes were already sanitised by the | ||
| // normalisers (sanitizeRuntimeContext). | ||
| const input = scrubOccurrence(raw); |
There was a problem hiding this comment.
🟡 [ai] Browser filename attributes are not scrubbed before storage or forwarding — Risk: 35/100
fingerprintAll applies scrubOccurrence, but that function only replaces errorType, message, stack, and route, leaving attributes untouched. The browser normalizer puts the query-stripped event.filename directly into attributes.filename; email addresses or secret-shaped values in a filename path therefore survive this new server-side pass and are serialized into the stored occurrence attributes (and sent onward with the occurrence). Scrub the filename or sanitize all occurrence attributes before they leave the normalizer.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/otlp-ingester/src/normalize-browser.ts,packages/otlp-ingester/src/redact.ts,packages/otlp-ingester/src/server.ts,packages/otlp-ingester/src/clickhouse.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Flagged by Autter security & observability checks.
| const pkg = createRequire(import.meta.url)("../package.json") as { version?: string }; | ||
| cachedVersion = typeof pkg.version === "string" ? pkg.version : "0.0.0"; | ||
| } catch { | ||
| cachedVersion = "0.0.0"; |
There was a problem hiding this comment.
🟡 [ai] Package-version lookup failure is cached as a real version — Risk: 25/100
If loading ../package.json fails (for example, a missing or malformed package file in a deployment artifact), this catch silently caches 0.0.0 and never retries or reports the lookup failure. createIngesterApp uses that value for the compatibility report and the x-autter-ingester-version response header, so every client sees a fabricated version and can diagnose a healthy newer ingester as obsolete; the dashboard also records the false version. Preserve observability and represent an unavailable version as unknown or fail startup instead of presenting the fallback as authoritative metadata.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/otlp-ingester/src/sdk-versions.ts,packages/otlp-ingester/src/server.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Make package-version lookup failure observable and avoid caching `0.0.0` as if it were the actual ingester version; either fail startup with a clear error or propagate an explicit unknown version that compatibility consumers handle safely.
Flagged by Autter security & observability checks.
| return; | ||
| } | ||
| mergeInfo(info); | ||
| for (const route of routesFor(current.features)) current.pendingProbes.add(route); |
There was a problem hiding this comment.
🟡 [ai] An inconclusive legacy route probe is permanently treated as pending — Risk: 25/100
If a legacy ingester returns 404 for /v1/compat but a route probe times out or otherwise fails, probeRoute returns null and fetchIngesterCompat omits that route from info.routes. runCheck nevertheless adds every feature route to pendingProbes; evaluateAndWarn then excludes pending routes from future probes, and the set is never cleared. A transient failure on the one-time startup check therefore prevents this process from ever detecting or warning that the legacy ingester lacks the feature route. Only mark successful/in-flight probes pending and clear the marker after an inconclusive result so it can be retried.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/runtime-node/src/compat.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Flagged by Autter security & observability checks.
|
| /** `Authorization: …`, `Cookie: …`, `Set-Cookie: …` (raw header text, JSON, | ||
| * util.inspect): the whole value goes, since cookies and auth schemes | ||
| * contain spaces and semicolons. */ | ||
| const HEADER_VALUE_RE = |
There was a problem hiding this comment.
🟡 [ai] Cookie/Authorization header redaction stops at the first quote, leaking credentials in all SDK scrubbers — Risk: 30/100
The shared Authorization: / Cookie: / Set-Cookie: header-text pattern masks only up to the first " or ' character. Any header value that contains a quote leaves the rest of the credential in clear text. This affects the Node SDK, browser SDK, Python adapter and ingester scrubbers identically, and the shared test vectors never exercise it.
HEADER_VALUE_RE is /((?:^|[^\w-])(?:proxy-)?(?:authorization|(?:set-)?cookie)["']?\s*[:=]\s*["']?)[^"'\r\n]+/gim. The optional opening quote is consumed into the head group, and the value is [^"'\r\n]+, so matching ends at the first quote character inside the value. Results from running the Node redactor:
Cookie: theme=dark; session="SECRETVALUE1234"; lang=enbecomesCookie: [redacted]"SECRETVALUE1234"; lang=en.Authorization: Digest username="bob", realm="api", response="6629..."becomesAuthorization: [redacted]"bob", realm="api", response="6629...".{\"headers\":{\"cookie\":\"sid=SECRETVALUE\"}}is untouched, because the backslash between the key and its closing quote prevents["']?\s*[:=]from matching.
The later SECRET_ASSIGNMENT_RE pass does not rescue these: it only matches secret-named keys such as session_id, and session="..." is not one. The same regex is duplicated in the ingester, browser and Python implementations, so the server-side backstop leaks identically. The fix replaces the value group with a quote-aware alternation (a double-quoted string with escapes, a single-quoted string, or the rest of the line), tolerates a backslash before the key's closing quote, and re-emits the surrounding quote characters when masking. It adds no nested quantifier on the same character class, and a 60k-char adversarial input ran in under 5 ms.
- Call
redactTextfrompackages/runtime-node/src/redact.ts(orredact_textinadapters/python/redact.py) withCookie: theme=dark; session="SECRETVALUE1234"; lang=en. - Observe that
"SECRETVALUE1234"survives in the output. - Call it with
Authorization: Digest username="bob", realm="api", response="6629fae49393a05397450978507c4ef1"and observe that everything afterDigestsurvives. - Call it with the JSON-escaped string
{\"cookie\":\"sid=SECRETVALUE\"}and observe it is returned unchanged.
Evidence:
Before the fix the output is Cookie: [redacted]"SECRETVALUE1234"; lang=en. After the fix the output is Cookie: [redacted], Authorization: "[redacted]" retry, and {\"cookie\":[redacted]. Node (95), browser (44) and ingester (117) test suites and the Python unit tests pass with the fix and the two new shared vectors.
Checked key-name matching, which still masks structured cookie/authorization attributes wholesale, so only free-text header dumps are affected. The common unquoted Cookie: a=b; c=d and Authorization: Bearer x forms are redacted correctly. The leak needs a quote in the header value, or JSON-escaped header text.
Proof of concept:
import { redactText } from "./packages/runtime-node/src/redact.ts";
for (const s of [
'Cookie: theme=dark; session="SECRETVALUE1234"; lang=en',
'Authorization: Digest username="bob", realm="api", response="6629fae49393a05397450978507c4ef1"',
'{\\"cookie\\":\\"sid=SECRETVALUE\\"}',
]) console.log(redactText(s));🛠 AI fix prompt (copy & paste into your coding agent)
Replace the header value capture with a quote-aware alternation in all four implementations (Node, browser, ingester, Python), allow an optional backslash after the header name, and re-emit the surrounding quote when masking. Add the quoted-cookie and quoted-Authorization cases to the shared test-vectors/redaction.json so every implementation is checked for parity.
Impact to account for:
Credentials embedded in header text inside exception messages, stacks, URLs or logs are exported to the telemetry backend, ClickHouse and the sink webhook unredacted. Demonstrated leaks: the remaining cookies after a quoted cookie (`session="..."`), the full RFC 7616 Digest `Authorization` parameters (username, nonce, response), and every JSON-escaped header dump (`{\"cookie\":\"sid=...\"}`, the form produced by double-serialised JSON). Impact is limited to secrets that happen to appear as header text in strings, and not every deployment emits quoted cookie values, so severity is low.
Flagged by Autter security & observability checks.
🤖 Release Notes CuratorImpact: minor. This adds user-facing capabilities and doesn't break the public API. The additions are secret scrubbing in the browser SDK, the relay and the Python adapter, a new Changelog entry Security
Added
Custom agent · runs after review · configured in Autter |
| * adapters/python/redact.py. | ||
| */ | ||
|
|
||
| type AttrValue = |
There was a problem hiding this comment.
🟡 [deterministic] oxlint: eslint(no-unused-vars) — Risk: 30/100
packages/runtime-core/src/redact.ts:25:6: Type alias 'AttrValue' is declared but never used.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the oxlint `eslint(no-unused-vars)` issue at packages/runtime-core/src/redact.ts:25: packages/runtime-core/src/redact.ts:25:6: Type alias 'AttrValue' is declared but never used.
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-core/src/redact.ts:108:203: Unnecessary escape character '['
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the oxlint `eslint(no-useless-escape)` issue at packages/runtime-core/src/redact.ts:108: packages/runtime-core/src/redact.ts:108: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-core/src/redact.ts:509: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-core/src/redact.ts:509: packages/runtime-core/src/redact.ts:509:4: Empty fallbacks in spreads are unnecessary
Flagged by Autter security & observability checks.
| "@opentelemetry/sdk-trace-base": "^1.30.1", | ||
| "@opentelemetry/semantic-conventions": "^1.28.0" | ||
| }, | ||
| "bin": { |
There was a problem hiding this comment.
🟠 [deterministic] Vulnerable dependency: @grpc/grpc-js@1.14.4 — CVE-2026-101916 — Risk: 75/100
@grpc/grpc-js@1.14.4 (npm) is affected by CVE-2026-101916 (GHSA-m9gg-hp2v-232j) — high severity. @grpc/grpc-js: In certain configurations, getAuthContext can return unauthorized certificates as though they were authorized
🛠 AI fix prompt (copy & paste into your coding agent)
Upgrade `@grpc/grpc-js` past the version affected by CVE-2026-101916, or apply the advisory's mitigation. Details: https://osv.dev/GHSA-m9gg-hp2v-232j.
Flagged by Autter security & observability checks.
| resultCallback({ code: ExportResultCode.FAILED, error: error as Error }); | ||
| return; | ||
| } | ||
| this.inner.export({ ...metrics, resource }, resultCallback); |
There was a problem hiding this comment.
🟠 [ai] Metric datapoint attributes bypass the redactor — Risk: 65/100
The metric exporter only scrubs the resource and then forwards the original ResourceMetrics with all scopeMetrics unchanged. Those metric datapoints can contain attributes supplied by application code through the SDK's meter provider, not only the low-cardinality attributes produced internally. A datapoint such as { password: "secret" } therefore reaches the OTLP exporter and ingester without the redaction promised for telemetry leaving the process. Scrub datapoint attributes (and other free-text metric fields that can carry attributes) before forwarding.
⚠ 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)
Transform every metric datapoint's attributes with the active redactor before calling the inner exporter; preserve the metric values and aggregation data.
Flagged by Autter security & observability checks.
| ) | ||
| ] | ||
| # Numeric GenAI usage counters keep their value even though "token" matches. | ||
| _USAGE_KEY = re.compile(r"(^|\.)(input|output|total|prompt|completion)_?tokens$|token_count$", re.I) |
There was a problem hiding this comment.
🟠 [ai] Python redaction masks valid GenAI usage counters — Risk: 54/100
The usage-counter exemption only recognizes input/output/total/prompt/completion token keys and token_count. Other canonical counters such as cache_read_tokens, cache_creation_tokens, cached_tokens, reasoning_tokens, and gen_ai.usage.cache_read_input_tokens do not match this regex, while _SENSITIVE_KEYS matches their token substring. Consequently _redact_value replaces their numeric values with [redacted], so applications using the Python adapter's recommended redact_attributes path lose those usage values before export and downstream cost/usage tracking cannot recover them. The Node redactor explicitly preserves these canonical counters.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
adapters/python/redact.py,packages/runtime-core/src/redact.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Expand the Python usage-key exemption to cover the same canonical non-negative numeric token counters preserved by the runtime-core redactor, while continuing to mask arbitrary sensitive token fields.
Flagged by Autter security & observability checks.
| ); | ||
| if (unprobed.length) { | ||
| const current = state; | ||
| for (const route of unprobed) current.pendingProbes.add(route); |
There was a problem hiding this comment.
🟡 [ai] Failed legacy-route probe permanently suppresses compatibility warning — Risk: 35/100
When a legacy ingester has no /v1/compat endpoint, a route probe can time out or fail to connect; probeRoute represents that as null. This code leaves the route in pendingProbes, while the following result loop omits null results from info.routes. Later calls to evaluateAndWarn filter out routes already in pendingProbes, so the route is never retried and the caller gets no incompatibility warning even if the legacy ingester does not support the feature and drops its data. Clear the pending marker on indeterminate results and allow a bounded retry or surface the check as unknown with observable diagnostics.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/runtime-node/src/sinks.ts,packages/runtime-node/src/compat.ts,packages/otlp-ingester/src/compat-manifest.json
🛠 AI fix prompt (copy & paste into your coding agent)
Ensure legacy-route probes that return null do not remain permanently pending: clear pending state and permit a bounded retry, or expose/log an explicit unknown check result so callers do not mistake an unchecked route for compatibility.
Flagged by Autter security & observability checks.
|
🟡 [deterministic] Tests failed: packages/runtime-node/test/autoflush.test.mjs — Risk: 40/100
Failing tests
🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
|
🟡 [deterministic] Tests failed: packages/runtime-node/test/logger.test.mjs — Risk: 40/100
Failing tests
🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
|
🟡 [deterministic] Tests failed: packages/runtime-node/test/redact.test.mjs — Risk: 40/100
Failing tests
🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
|
🟡 [deterministic] Tests failed: packages/runtime-node/test/relay.test.mjs — Risk: 40/100
Failing tests
🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
|
🟡 [deterministic] Tests failed: packages/runtime-node/test/sinks.test.mjs — Risk: 40/100
Failing tests
🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
| @@ -0,0 +1,218 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-node/test/compat.test.mjs — Risk: 40/100
packages/runtime-node/test/compat.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/compat.test.mjs'.
Failing tests
- the file failed before any test reported — The build output tries to import @opentelemetry/exporter-metrics-otlp-http but the package is not found in the environment.
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 '@opentelemetry/exporter-metrics-otlp-http' imported from /tmp/autter-agentic-QiOanc/packages/runtime-node/dist/index.js
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:331:9)
at packageResolve (node:internal/modules/esm/resolve:784:25)
at moduleResolve (node:internal/modules/esm/resolve:873:18)
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:705:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:725:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:763:56)
at #resolve (node:internal/modules/esm/loader:687:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:607:35)
at ModuleJob.syncLink (node:internal/modules/esm/module_job:276:33) {
code: 'ERR_MODULE_NOT_FOUND'
}
Node.js v24.21.0
✖ test/compat.test.mjs (95.563615ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms …
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-node/test/compat.test.mjs fails when run with `node --test 'test/compat.test.mjs'`: 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 '@opentelemetry/exporter-metrics-otlp-http' imported from /tmp/autter-agentic-QiOanc/packages/runtime-node/dist/index.js
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:331:9)
at packageRe. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
| @@ -0,0 +1,68 @@ | |||
| // Relay-only process (no initAutterServer): the relay checks browser | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-node/test/relay-compat.test.mjs — Risk: 40/100
packages/runtime-node/test/relay-compat.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/relay-compat.test.mjs'.
Failing tests
- the file failed before any test reported — The build output tries to import @opentelemetry/exporter-metrics-otlp-http but the package is not found in the environment.
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 '@opentelemetry/exporter-metrics-otlp-http' imported from /tmp/autter-agentic-QiOanc/packages/runtime-node/dist/index.js
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:331:9)
at packageResolve (node:internal/modules/esm/resolve:784:25)
at moduleResolve (node:internal/modules/esm/resolve:873:18)
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:705:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:725:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:763:56)
at #resolve (node:internal/modules/esm/loader:687:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:607:35)
at ModuleJob.syncLink (node:internal/modules/esm/module_job:276:33) {
code: 'ERR_MODULE_NOT_FOUND'
}
Node.js v24.21.0
✖ test/relay-compat.test.mjs (85.056993ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ durati…
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-node/test/relay-compat.test.mjs fails when run with `node --test 'test/relay-compat.test.mjs'`: 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 '@opentelemetry/exporter-metrics-otlp-http' imported from /tmp/autter-agentic-QiOanc/packages/runtime-node/dist/index.js
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:331:9)
at packageRe. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
Autter task list
Generated from PR diff, blast radius, and context. Issues found
135 additional non-blocking finding(s) are available in the Autter review. Also detected but not listed above: 118 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. |
Fixes from external tester feedback. Two commits.
1. Scrub secrets before telemetry leaves the process (
7d936c6)Before: the SDKs scrubbed custom attributes but sent error messages and stack traces unscrubbed, so
connect ECONNREFUSED postgres://admin:pass@…went out as-is. That data later reaches LLM prompts.Now:
captureMessage, captures made before init, errors insidewithProcessSpan, and LLM-call errors and attributes. They also scrub at export time, which covers third-party spans and URLs with?token=. The browser relay scrubs message, stack, name and nested context.adapters/python/redact.py). Before, only emails were scrubbed.redis://:pass@and passwords containing@),sk-proj-/ Stripe /AIza/github_pat_/ GitLab keys, private keys, secret query parameters, card numbers, and sensitive key names at any depth.test-vectors/redaction.json, runs against all four implementations.mainthat could block Node for about 1 s on a 32 KB input.2. Automatic version compatibility check (
c602bc0)Before: users had to match SDK, ingester and platform versions by hand. A mismatch failed silently; for example, a pre-1.4.0 ingester returns 404 on
/v1/logsand the SDK quietly retried.Now:
packages/otlp-ingester/src/compat-manifest.json, shared by every component.x-autter-ingester-versionheader to every response;GET /v1/compat(no tenant data);0012-runtime-compat.telemetry.distro.*. One non-blocking check per process prints a single clear warning per incompatible feature, with the fix. It never throws or delays startup, and it can be turned off withcompatCheck: falseorAUTTER_COMPAT_CHECK=0.npx @autter/runtime-node doctor. Exits 0 when compatible, 1 on a mismatch, 2 when unreachable. Python haspython3 compat.py doctor.docs/COMPATIBILITY.md, linked from the feature docs.Verification (on the combined branch)
npm run build: passes.Deploy notes
🤖 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
Secret scrubbing was uneven across the browser, Node and Python SDKs and the ingester, so secret-shaped values could still reach stored or forwarded telemetry. There was also no automated way to tell when an SDK and the ingester are on incompatible versions.
What changes for users
Every SDK and the ingester now redact secrets and PII with shared rules and test vectors, and operators can add their own patterns. SDKs and the ingester exchange version information so incompatibilities can be detected and reported through a compat endpoint and a doctor CLI, backed by a ClickHouse record of SDK versions seen.
Implementation and review notes
runtime-core/src/redact.tsand mirrored by newredact.tsin the ingester andredact.pyin the Python adapter.test-vectors/redaction.jsonis meant to keep them consistent, with vector tests in the Node, browser and Python suites.runtime-node/src/redact-exporter.tsadds a last-line redaction wrapper around the OTLP trace exporter. A prior review noted that span links and resource attributes may pass through unscrubbed, so check that.compat.ts,compat-manifest.json,sdk-versions.tsand aGET /v1/compatroute. Every response carries an ingester-version header, and SDK sightings are recorded fire-and-forget so they never delay or fail ingest.sdk_versionsandingester_infotables inschemaStatements()and a new migration inmigrations.ts. Confirm the migration is idempotent and append-only, as the repo rules require.config.tsaddsAUTTER_REDACT_VALUE_PATTERNSandAUTTER_REDACT_KEY_PATTERNS. These parse as JSON and throw a plainErroron bad input to fail startup, which a past review flagged as differing from the typed-error convention.test-vectors/redaction.jsonandadapters/python/test_redact.pycontain secret-shaped fixtures (JWT, Postgres URL, private key) that scanners flag. They appear to be test data, so confirm they are clearly fake.Changes
Redaction (all surfaces)
packages/runtime-core/src/redact.tsis reworked (+199/-72) and is the shared rule set for the Node and browser SDKs.packages/runtime-node/src/redact.tsandpackages/runtime-node/src/logger.tspick up the new options.LoggerConfiggains additional fields, andAutterServerOptionsinruntime-node/src/server.tsgains redaction options.runtime-next/src/server.tsre-exports the new surface.packages/runtime-node/src/redact-exporter.tsis new. It wraps the OTLP trace exporter so span and event attributes are scrubbed immediately before export.runtime-node/src/sinks.tsandruntime-core/src/otlp-logs.tscarry the new SDK version/resource fields.packages/runtime-browser/src/index.tsadds in-bundle value and key scrubbing (+102/-23) and a newAutterBrowserOptionsentry. The browser tracker must stay zero-dependency and under the 5 KB brotli budget, which CI checks vianpm run size.packages/runtime-node/src/relay.tsextendssanitizeBrowserPayloadand the relay handlers with the same scrubbing, so secrets under benign context keys no longer pass through the same-origin relay.packages/otlp-ingester/src/redact.tsis new and is applied viaconfigureRedaction(config)andscrubOccurrenceinserver.ts.normalize-browser.ts,normalize-otlp.ts,context.tsandllm.tsnow call the sharedscrubText/isSensitiveKeyinstead of local regexes and a localscrubContext. This removes duplicated logic.packages/otlp-ingester/src/config.tsaddsredactValuePatterns/redactKeyPatterns, read fromAUTTER_REDACT_VALUE_PATTERNSandAUTTER_REDACT_KEY_PATTERNS. Invalid JSON or an invalid regex fails startup.adapters/python/redact.pyis new andadapters/python/caught_exceptions.pynow uses it.test-vectors/redaction.jsonis the cross-language contract. It is exercised byredact-vectors.test.mjs(browser),redact.test.mjs(node),redact.test.ts(ingester) andtest_redact.py(Python).SDK/ingester compatibility
packages/otlp-ingester/src/compat.ts,compat-manifest.jsonandsdk-versions.tsare new. They providebuildCompatReport/evaluateCompat, anSdkVersionTracker, andsdkSightingsFromOtlp.packages/otlp-ingester/src/server.ts:GET /v1/compatroute.recordSdks()after successful ingest. This is fire-and-forget and never delays or fails the request.reportIngesterInfo()to once per 6 hours.packages/otlp-ingester/src/clickhouse.tsaddsinsertSdkVersions,insertIngesterInfoandschemaStatus().schemaStatements()andmigrations.tsadd thesdk_versionsandingester_infotables.coded-errors.test.tsis updated for the new migration ordering.packages/otlp-ingester/src/normalize-browser.tskeeps the optionalsdkversion field inbrowserPayloadSchema.packages/runtime-node/src/compat.ts,doctor.tsandcli.tsare new. They add the SDK-side compat check and the doctor CLI, wired throughruntime-node/package.jsonandtsup.config.ts.adapters/python/compat.pyis the Python counterpart..github/workflows/ci.ymlgets one added line, andpackage-lock.jsongets a small update.Docs
docs/COMPATIBILITY.mdand updates the READMEs,CHANGELOG.md,ARCHITECTURE.md,CONTINUOUS-DETECTION.md,OPERATION-LOGGING.md,MEMORY-PRESSURE.md,ENDPOINT-REGRESSIONS.mdandINTEGRATIONS.md.Breaking changes / contract notes
/v1/browserv1 payload, OTLP surface and ClickHouse schema changes are additive (new tables with defaults, a new route, a new response header, an optionalsdkfield).[redacted].AUTTER_REDACT_*env values now fail ingester startup. Validate these before rolling out.Acceptance Criteria
test-vectors/redaction.jsonproduces the same redacted output in the Node, browser, ingester and Python implementations, and all four vector suites pass.{context:{note:"sk-…"}}) sent through the browser relay and to/v1/browseris absent or redacted in the forwarded payload and in ClickHouse.redact-exporter.tsbefore export. Spanlinksandresourceattributes are scrubbed too, or the gap is explicitly documented and tracked.AUTTER_REDACT_VALUE_PATTERNSandAUTTER_REDACT_KEY_PATTERNSadd to the built-in patterns. Malformed input fails startup with a clear message.GET /v1/compatreturns the ingester version, schema level and applied/pending migrations, and every ingester response includes the ingester-version header.sdk_versionswithout adding failure modes to ingest. A ClickHouse failure there is logged and ingest still succeeds.schemaStatements()) and a migrated database end with the samesdk_versionsandingester_infoshape.doctor.test.mjs,compat.test.mjs,test_compat.py,server.compat.test.ts).npm run buildpasses and the browser bundle stays under the 5 KB brotli budget with zero dependencies.Test Plan
npm run buildat the repo root and confirmnpm run size -w @autter/runtime-browserstays within budget.redact.test.ts,compat.test.ts,server.compat.test.ts,coded-errors.test.ts), the runtime-node tests (redact.test.mjs,compat.test.mjs,doctor.test.mjs,relay-compat.test.mjs,autoflush.test.mjs), the runtime-browser tests (redact-vectors,redact-hardening,redact-e2e) andpython -m pytest adapters/python. All must pass.curl -i localhost:4318/v1/compat. Check that the ingester-version header is present and that the schema level and migrations look right./v1/browserpayload using the documented curl request, withcontextcontaining{"note":"sk-<fake key>","debug":"AKIA<fake>"}and a fake JWT inmessage. Read the row back from ClickHouse and confirm no secret survives inmessage,stack,routeorattributes.redact-exporter.ts.AUTTER_REDACT_VALUE_PATTERNS='["internal-[0-9]{6}"]'and confirm a matching value is masked. Then setAUTTER_REDACT_VALUE_PATTERNS='not json'and confirm startup fails with a clear error.docs/COMPATIBILITY.md.sdk_versionsandingester_infoin ClickHouse. Confirm the SDK rows are present and thatreportIngesterInfowrites at most once per throttle window.Rollback Plan
mainand redeploy the previous ingester image. For single-server, re-run the deploy so only the ingester is recreated. For ECS, register the prior task definition revision and roll the service.@autter/runtime-node,@autter/runtime-browser,@autter/runtime-nextand the Python adapter. Package versions are independent.AUTTER_REDACT_VALUE_PATTERNSandAUTTER_REDACT_KEY_PATTERNSand restart.sdk_versionsandingester_infotables are additive, and the migration is recorded inschema_migrations. Leave them in place, and do not edit or delete the shipped migration. If cleanup is wanted, drop the two tables manually and append a corrective migration./v1/browserand OTLP requests still write rows. Also confirm thatGET /v1/compatis no longer served, if the revert covers it.Related Issues
No linked issue was identified from the branch name (
fix/tester-feedback-batch), the PR title or the supplied diff context.Written for commit 0f05407. Summary will update on new commits.