Repository navigation
Request summaries, coded errors and edge SDK (Runtime 1.5.0) - #25
Conversation
…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>
🚦 Pre-merge checks ·
|
| Check | Status | Explanation |
|---|---|---|
| Too many files changed | This PR changes 96 files (limit 25 lowered from 50). Consider splitting this PR by area: packages (74 files), docs (8 files), examples (6 files), (root) (5 files), .github (2 files), scripts/ensure-built.mjs (1 file). This PR touches high-impact code whose failure cascades downstream: `packages/otlp-ingester/src/types. ... [truncated 58 chars] | |
| Too many lines changed | This PR changes 10922 lines (limit 500 lowered from 1000). Splitting into smaller, focused PRs makes review more reliable. This PR touches high-impact code whose failure cascades downstream: packages/otlp-ingester/src/types.ts (blast 70, fan-in 11), so the bar for review is lower. |
|
| Mixed concerns (refactor + behavior change) | The PR moves redaction into the new runtime-core package, leaving runtime-node/src/redact.ts as a re-export. That is a pure refactor. The same PR adds request summaries, coded errors, a new code-v1 fingerprint that regroups issues, and sink payload changes that omit fields when empty. It also reworks logger.ts (632 add ... [truncated 516 chars] | |
| Removed observability | 3 potential issue(s) detected (max risk 61/100): packages/runtime-node/src/logger.ts:251, packages/runtime-node/src/logger.ts:266, packages/runtime-node/src/logger.ts:376. | |
| Possible TOCTOU in critical path | 1 potential issue(s) detected (max risk 20/100): packages/otlp-ingester/src/server.ts:348. | |
| PII stored without application-level encryption | 3 potential issue(s) detected (max risk 93/100): packages/runtime-edge/src/index.ts:72, packages/runtime-node/src/logging-only.ts:51, packages/runtime-node/src/sinks.ts:38. | |
| Lockfile resolution / integrity tampered | 1 potential issue(s) detected (max risk 75/100): package-lock.json:3438. | |
| Possible dependency-confusion attack | 1 potential issue(s) detected (max risk 39/100): examples/edge-worker/package.json:12. | |
| Inconsistent error handling | 1 potential issue(s) detected (max risk 20/100): packages/runtime-core/src/fingerprint-code.ts:19. | |
| Hallucinated import (package not installed) | 1 potential issue(s) detected (max risk 100/100): packages/otlp-ingester/src/log-promotion.ts:3. | |
| Generic placeholder identifier in production logic | 12 potential issue(s) detected (max risk 51/100): packages/otlp-ingester/src/error-fields.ts:44, packages/runtime-core/src/carrier.ts:26, packages/runtime-core/src/context.ts:117, packages/runtime-core/src/fingerprint-code.ts:20, packages/runtime-core/src/redact.ts:205. | |
| Comment contradicts or fabricates code behaviour | 1 potential issue(s) detected (max risk 58/100): packages/otlp-ingester/src/fingerprint.ts:312. | |
| Access token in source | 1 potential secret(s) detected (max risk 100/100): packages/runtime-core/test/core.test.mjs:257. | |
| Database connection URL with embedded credentials | 1 potential issue(s) detected (max risk 90/100): packages/runtime-core/test/core.test.mjs:256. | |
| Co-change coupling | 1 file(s) changed without a partner they usually change with. | |
| Intent vs. implementation | The visible ingester-side changes (code-based fingerprinting, error-field lifting, request-id linking, log TTL) match the stated intent, but most of the SDK deliverables sit in omitted files and could not be verified. Requirements: 4 implemented, 2 partial. 2 change(s) with no backing requirement. (Intent from product ... [truncated 25 chars] | |
| Complexity Guard | 1 finding(s) on changed lines. | |
| Release Notes Curator | 20 root-cause issue(s) detected (max risk 100/100): packages/otlp-ingester/src/fingerprint.ts:280, packages/otlp-ingester/src/normalize-browser.ts:211, packages/runtime-core/test/core.test.mjs:32, packages/runtime-node/test/requests.test.mjs:48, packages/otlp-ingester/src/sink.ts:437. |
✅ Passed checks (185)
| Check | Status | Explanation |
|---|---|---|
| Too many unrelated chapters | ✅ Passed | 8 chapter(s) detected, within the limit of 6. |
| Generated files hiding real changes | ✅ Passed | Generated-file volume (68 lines) does not obscure the 10854 hand-written line(s). |
| Missing PR context | ✅ Passed | PR context looks sufficient. |
| Migration + app logic + UI combined in one PR | ✅ Passed | The PR includes ClickHouse migrations (migrations.ts, clickhouse.ts) and application logic, but it has no user-interface changes. The browser SDK and the edge example are libraries and sample code, not UI components or screens. |
| 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. |
| Silent exception swallowing | ✅ Passed | No silent exception swallowing issues remaining after verification. |
| Unhandled promise rejection | ✅ Passed | No unhandled promise rejection 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. |
| 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 detected. |
| Rate limiting removed | ✅ Passed | No rate limiting removed issues detected. |
| Batch size limit not detected | ✅ Passed | No batch size limit not detected issues remaining after verification. |
| Pagination not detected | ✅ Passed | No pagination not detected issues remaining after verification. |
| 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. |
| Dependency runs install-time lifecycle script | ✅ Passed | No dependency runs install-time lifecycle script issues detected. |
| Lockfile resolves a dependency the manifest does not declare | ✅ Passed | No lockfile resolves a dependency the manifest does not declare issues remaining after verification. |
| 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 remaining after verification. |
| 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. |
| 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. |
| Multi-write without transaction wrapper | ✅ Passed | No multi-write without transaction wrapper issues detected. |
| New feature shipped without feature flag | ✅ Passed | No new feature shipped without feature flag issues detected. |
| Module placed in the wrong workspace package | ✅ Passed | No module placed in the wrong workspace package issues detected. |
| 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. |
| 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. |
| 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. |
| 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. |
| Hardcoded secret | ✅ Passed | No hardcoded secret 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. |
| Unsafe cookie / session settings | ✅ Passed | No unsafe cookie / session settings issues detected. |
| Invisible Unicode / Trojan Source characters | ✅ Passed | No invisible unicode / trojan source characters issues detected. |
| API key in source | ✅ Passed | No api key in source detected. |
| Private key in source | ✅ Passed | No private key 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 findings. |
| 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 remaining after verification. |
| 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 remaining after verification. |
| 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 additional explanation was reported. |
| Resource leak risk | ✅ Passed | No resource leak risk issues remaining after verification. |
| Data integrity risk | ✅ Passed | No additional explanation was reported. |
| Maintainability issue | ✅ Passed | No additional explanation was reported. |
| Redundant alias / duplicate import | ✅ Passed | No additional explanation was reported. |
| Redundant type construct | ✅ Passed | No redundant type construct issues remaining after verification. |
| Unnecessary type assertion | ✅ Passed | No unnecessary type assertion issues remaining after verification. |
| 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 | 54% AI-authored (claude:claude-opus-5-5: 54%), 0% human, 46% untracked. (partial coverage — some commits not tracked) |
| Dependency Auditor | ✅ Passed | No additional explanation was reported. |
| Bundle Size Monitor | ✅ Passed | No bundle size monitor issues remaining after verification. |
This comment is updated automatically whenever Autter reviews a new PR revision.
| return handler(request, env as Env, ctx as Ctx, runtime.rt); | ||
| if (!opts.apiKey && !warnedNoKey) { | ||
| warnedNoKey = true; | ||
| console.warn("[autter-runtime-edge] no apiKey: records are not exported"); |
There was a problem hiding this comment.
🔴 [deterministic] Sensitive data exposure — Risk: 90/100
Sensitive data (credential, internal id, or user secret) flows into a client-bound response or log line without redaction. Blast radius — if this is exploited it cascades to the downstream usage that depends on this file: endpoints POST /checkout, GET /healthz, GET /api/users/:id, POST /api/checkout, POST /api/users/:id, GET /boom; dependent files packages/otlp-ingester/src/fingerprint.ts, packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/sink.ts, packages/otlp-ingester/src/server.sink.test.ts, packages/otlp-ingester/src/sink.test.ts, packages/otlp-ingester/src/llm.ts, packages/otlp-ingester/src/index.ts, packages/otlp-ingester/src/auth.ts.
References:
- https://cwe.mitre.org/data/definitions/200.html — | | | | --- | --- | | Home > CWE List > CWE-200: Exposure of Sensitive Information to an Unauthorized Actor (4.20) | | --- | | | | | --- | --- | --- | | | | | | | | | | --- |
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- API endpoints:
POST /checkout,GET /healthz,GET /api/users/:id,POST /api/checkout,POST /api/users/:id,GET /boom - Dependent files:
packages/otlp-ingester/src/fingerprint.ts,packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts,packages/otlp-ingester/src/llm.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `packages/runtime-edge/src/index.ts` around line 265, sensitive data (credential, internal id, raw stack trace, or user PII) is sent to a client-bound channel (response body, log line, error payload). Filter the output through a redaction helper / serializer that allow-lists the public-safe fields; for errors, return a generic message + a server-side correlation id and log the trace internally. Blast radius — if this is exploited it cascades to the downstream usage that depends on this file: endpoints `POST /checkout`, `GET /healthz`, `GET /api/users/:id`, `POST /api/checkout`, `POST /api/users/:id`, `GET /boom`; dependent files `packages/otlp-ingester/src/fingerprint.ts`, `packages/otlp-ingester/src/normalize-otlp.ts`, `packages/otlp-ingester/src/sink.ts`, `packages/otlp-ingester/src/server.sink.test.ts`, `packages/otlp-ingester/src/sink.test.ts`, `packages/otlp-ingester/src/llm.ts`, `packages/otlp-ingester/src/index.ts`, `packages/otlp-ingester/src/auth.ts`.
Flagged by Autter security & observability checks.
| const app = express(); | ||
| app.use(autterRequests({ ignore: ["/healthz"] })); | ||
| app.use(express.json()); | ||
| app.get("/healthz", (_req, res) => res.send("ok")); |
There was a problem hiding this comment.
🟠 [deterministic] Sensitive data exposure — Risk: 69/100
Sensitive data (credential, internal id, or user secret) flows into a client-bound response or log line without redaction. Blast radius — if this is exploited it cascades to the downstream usage that depends on this file: endpoints POST /checkout, GET /healthz, GET /api/users/:id, POST /api/checkout, POST /api/users/:id, GET /boom; dependent files packages/otlp-ingester/src/fingerprint.ts, packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/sink.ts, packages/otlp-ingester/src/server.sink.test.ts, packages/otlp-ingester/src/sink.test.ts, packages/otlp-ingester/src/llm.ts, packages/otlp-ingester/src/index.ts, packages/otlp-ingester/src/auth.ts.
References:
- https://cwe.mitre.org/data/definitions/200.html — | | | | --- | --- | | Home > CWE List > CWE-200: Exposure of Sensitive Information to an Unauthorized Actor (4.20) | | --- | | | | | --- | --- | --- | | | | | | | | | | --- |
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- API endpoints:
POST /checkout,GET /healthz,GET /api/users/:id,POST /api/checkout,POST /api/users/:id,GET /boom - Dependent files:
packages/otlp-ingester/src/fingerprint.ts,packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts,packages/otlp-ingester/src/llm.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `packages/runtime-node/test/requests.test.mjs` around line 48, sensitive data (credential, internal id, raw stack trace, or user PII) is sent to a client-bound channel (response body, log line, error payload). Filter the output through a redaction helper / serializer that allow-lists the public-safe fields; for errors, return a generic message + a server-side correlation id and log the trace internally. Blast radius — if this is exploited it cascades to the downstream usage that depends on this file: endpoints `POST /checkout`, `GET /healthz`, `GET /api/users/:id`, `POST /api/checkout`, `POST /api/users/:id`, `GET /boom`; dependent files `packages/otlp-ingester/src/fingerprint.ts`, `packages/otlp-ingester/src/normalize-otlp.ts`, `packages/otlp-ingester/src/sink.ts`, `packages/otlp-ingester/src/server.sink.test.ts`, `packages/otlp-ingester/src/sink.test.ts`, `packages/otlp-ingester/src/llm.ts`, `packages/otlp-ingester/src/index.ts`, `packages/otlp-ingester/src/auth.ts`.
Flagged by Autter security & observability checks.
| ...(traceId ? { traceId } : {}), | ||
| ...(route ? { route } : {}), | ||
| ...(method ? { method } : {}), | ||
| ...(statusCode ? { statusCode } : {}), |
There was a problem hiding this comment.
🟠 [ai] statusCode 0 dropped by truthiness guard in sinkOccurrence — Risk: 50/100
sinkOccurrence emits the optional request-context fields with truthiness guards, and ...(statusCode ? { statusCode } : {}) treats a valid numeric status of 0 as absent. Browsers report status: 0 for opaque (no-cors) responses and failed/blocked requests, and the previous ...o spread passed that value through, so sink consumers now lose the request context for exactly those failure cases. Guard on statusCode !== undefined && statusCode !== null (or statusCode != null) instead of truthiness; the string guards on traceId/route/method/errorCode/etc. are correct as-is.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Functions/symbols:
SinkForwarder.pump,SinkForwarder.enforceBounds,SinkForwarder.scheduleWake,SinkForwarder.untrack,SinkForwarder.maxBufferedBytes,SinkForwarder.send,normalizeTraces,normalizeMetrics - API endpoints:
POST /checkout,GET /healthz,GET /api/users/:id,POST /api/checkout,POST /api/users/:id,GET /boom - Dependent files:
packages/otlp-ingester/src/fingerprint.ts,packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts,packages/otlp-ingester/src/llm.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts,packages/otlp-ingester/src/normalize-browser.test.ts - Scopes:
@autter/otlp-ingester
🛠 AI fix prompt (copy & paste into your coding agent)
In `sinkOccurrence`, include `statusCode` whenever it is not null or undefined rather than testing its truthiness. Add a test confirming that a status code of 0 is preserved in the serialized occurrence. Blast radius — if this defect reaches production it can fail the downstream usage that depends on this file: functions `SinkForwarder.pump`, `SinkForwarder.enforceBounds`, `SinkForwarder.scheduleWake`, `SinkForwarder.untrack`, `SinkForwarder.maxBufferedBytes`, `SinkForwarder.send`, `normalizeTraces`, `normalizeMetrics`; endpoints `POST /checkout`, `GET /healthz`, `GET /api/users/:id`, `POST /api/checkout`, `POST /api/users/:id`, `GET /boom`; scopes `@autter/otlp-ingester`; dependent files `packages/otlp-ingester/src/fingerprint.ts`, `packages/otlp-ingester/src/normalize-otlp.ts`, `packages/otlp-ingester/src/server.sink.test.ts`, `packages/otlp-ingester/src/sink.test.ts`, `packages/otlp-ingester/src/llm.ts`, `packages/otlp-ingester/src/index.ts`, `packages/otlp-ingester/src/auth.ts`, `packages/otlp-ingester/src/normalize-browser.test.ts`.
Flagged by Autter security & observability checks.
🧭 PR hygiene & process suggestionsAutter has 5 suggestion(s) about the shape of this PR (size, scope, reviewability). These are process guidance — not code defects — so they are consolidated here instead of posted as inline comments on individual files. 🟠 PR changes too many files — split it — Risk: 75/100This PR changes 96 files (limit 25 lowered from 50 due to high-impact files). Highest-impact files: References:
🛠 AI fix prompt (copy & paste into your coding agent)🟠 PR mixes refactor and behavior change — Risk: 63/100The PR moves redaction into the new runtime-core package, leaving runtime-node/src/redact.ts as a re-export. That is a pure refactor. The same PR adds request summaries, coded errors, a new code-v1 fingerprint that regroups issues, and sink payload changes that omit fields when empty. It also reworks logger.ts (632 additions, 358 deletions). The refactor is mixed with behavior changes in high-fan-in ingester files such as types.ts, fingerprint.ts and config.ts. References:
🛠 AI fix prompt (copy & paste into your coding agent)🟡 Requirement only partially built — Risk: 38/100The PR description asked for: "SDK emits per-request summaries with x-request-id, runtimeContext, RuntimeError/defineRuntimeErrors/autterErrorResponse, carriers, AI usage rollup, initAutterLogging, and the new runtime-edge package", but the diff only partially delivers it. The SDK source files are in the omitted portion of the diff, so none of this could be verified. Only the ingester side is visible. 🛠 AI fix prompt (copy & paste into your coding agent)🟡 Change not tied to any stated requirement — Risk: 20/100Flagged in 2 places: No stated requirement asked for this: A per-tenant promotion-lookup rate limit (PROMOTION_LOOKUPS_PER_MINUTE) for /v1/logs promotion is added. The visible intent text does not mention it, though it may support log promotion.. Confirm it belongs in this PR rather than a separate change. 🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter PR-hygiene checks. 💡 2 suggestion(s) — conventions, hardening and hygiene, not defectsInline comments are reserved for concrete defects. These are things worth knowing that the diff does not prove wrong — a missing hardening layer, a convention the repo usually follows, a file that historically changes alongside one you touched. Skim, adopt what fits, ignore the rest.
|
🤖 Release Notes CuratorImpact: minor. This adds new user-facing capabilities: request summaries, coded errors, an edge SDK and test helpers. The changes are additive, and the wire payload stays at Changelog entry
Custom agent · runs after review · configured in Autter |
| }); | ||
| const [table] = await rows.json<{ engine_full: string }>(); | ||
| if (!table) return; | ||
| const current = /\bTTL\b[^]*?toIntervalDay\((\d+)\)/.exec(table.engine_full); |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/correctness/noEmptyCharacterClassInRegex — Risk: 55/100
The regular expression includes this negated empty character class.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/correctness/noEmptyCharacterClassInRegex` issue at packages/otlp-ingester/src/clickhouse.ts:265: The regular expression includes this negated empty character class.
Flagged by Autter security & observability checks.
| for (const ok of ["billing.declined", "a", "a.b.c.d", "inventory.reservation_timeout", "x1.y_2"]) { | ||
| assert.equal(validErrorCode(ok), ok); | ||
| } | ||
| for (const bad of ["ECONNRESET", "Billing.declined", "1abc", "a.b.c.d.e", "a..b", "a.", "a-b", "user.42@x", "", 42, null, "a" + "b".repeat(80)]) { |
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/coded-errors.test.ts:99: Template literals are preferred over string concatenation.
Flagged by Autter security & observability checks.
| ]), | ||
| ); | ||
| assert.equal(fields.errorCode, "billing.declined"); | ||
| assert.equal(fields.why!.length, 1000); |
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/coded-errors.test.ts:118: Forbidden non-null assertion.
Flagged by Autter security & observability checks.
| if (rawLink) declared.link = rawLink; | ||
| const rawRoute = | ||
| typeof attributes["http.route"] === "string" | ||
| ? attributes["http.route"].split(/[?#]/)[0]! |
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/logs.ts:176: Forbidden non-null assertion.
Flagged by Autter security & observability checks.
| ): { traceId: string; spanId: string; sampled: boolean } | null { | ||
| if (typeof value !== "string") return null; | ||
| const match = TRACEPARENT.exec(value.trim()); | ||
| if (!match || /^0+$/.test(match[1]!) || /^0+$/.test(match[2]!)) return null; |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/style/noNonNullAssertion — Risk: 55/100
Forbidden non-null assertion.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/style/noNonNullAssertion` issue at packages/runtime-core/src/carrier.ts:37: Forbidden non-null assertion.
Flagged by Autter security & observability checks.
| const regexes = patterns.map((pattern) => { | ||
| let source = ""; | ||
| for (let i = 0; i < pattern.length; i++) { | ||
| const char = pattern[i]!; |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/style/noNonNullAssertion — Risk: 55/100
Forbidden non-null assertion.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/style/noNonNullAssertion` issue at packages/runtime-core/src/request.ts:57: Forbidden non-null assertion.
Flagged by Autter security & observability checks.
| test("the queue is bounded per isolate", async () => { | ||
| let calls = 0; | ||
| const worker = withAutter( | ||
| { apiKey: "k", service: "edge-api", maxQueue: 3, fetch: async () => (calls++, new Response("", { status: 503 })) }, |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/style/noCommaOperator — Risk: 55/100
The comma operator is disallowed.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/style/noCommaOperator` issue at packages/runtime-edge/test/edge.test.mjs:127: The comma operator is disallowed.
Flagged by Autter security & observability checks.
| const requests = []; | ||
| 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/runtime-node/test/operations.test.mjs:26: The assignment should not be in an expression.
Flagged by Autter security & observability checks.
| try { | ||
| let carrier; | ||
| let producerId; | ||
| let producerTrace; |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/style/useConst — Risk: 55/100
This let declares a variable that is only assigned once.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/style/useConst` issue at packages/runtime-node/test/operations.test.mjs:92: This let declares a variable that is only assigned once.
Flagged by Autter security & observability checks.
| test("fastify plugin (structural, no dependency) summarises requests", async () => { | ||
| const runtime = captureRuntime(); | ||
| const hooks = {}; | ||
| const fastify = { addHook: (name, fn) => (hooks[name] = fn) }; |
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/requests.test.mjs:209: The assignment should not be in an expression.
Flagged by Autter security & observability checks.
|
|
||
| ## [1.5.0] - 2026-10-08 | ||
|
|
||
| Packages: `otlp-ingester` 1.5.0, `runtime-node` 1.5.0, `runtime-next` 1.5.0, `runtime-browser` 1.4.0, new `runtime-edge` 1.0.0. Additive: 1.4.0 code keeps working unchanged (golden-tested). |
There was a problem hiding this comment.
🟡 [deterministic] markdownlint: MD013 — Risk: 30/100
Line length: Expected: 80; Actual: 189
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the markdownlint `MD013` issue at CHANGELOG.md:5: Line length: Expected: 80; Actual: 189
Flagged by Autter security & observability checks.
| | `runtime_error_occurrences` | MergeTree | `(org_id, repository_id, fingerprint, occurred_at)` | 14 d | | ||
| | `runtime_spans` | MergeTree | `(org_id, repository_id, trace_id, started_at)` | 7 d | | ||
| | `runtime_logs` | ReplacingMergeTree | `(org_id, repository_id, occurred_at, event_id)` | 14 d | | ||
| | `runtime_logs` | ReplacingMergeTree | `(org_id, repository_id, occurred_at, event_id)` | `LOG_TTL_DAYS` (14 d) | |
There was a problem hiding this comment.
🟡 [deterministic] markdownlint: MD013 — Risk: 30/100
Line length: Expected: 80; Actual: 114
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the markdownlint `MD013` issue at docs/ARCHITECTURE.md:47: Line length: Expected: 80; Actual: 114
Flagged by Autter security & observability checks.
| } | ||
|
|
||
| function addContext(event: BrowserEvent, extra: Record<string, unknown>): void { | ||
| if (Object.keys(extra).length > 0) event.context = { ...(event.context || {}), ...extra }; |
There was a problem hiding this comment.
🟡 [deterministic] oxlint: unicorn(no-useless-fallback-in-spread) — Risk: 30/100
packages/runtime-browser/src/index.ts:175:55: 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-browser/src/index.ts:175: packages/runtime-browser/src/index.ts:175:55: Empty fallbacks in spreads are unnecessary
Flagged by Autter security & observability checks.
| * masked wholesale, before anything is exported. | ||
| */ | ||
|
|
||
| type AttrValue = |
There was a problem hiding this comment.
🟡 [deterministic] oxlint: eslint(no-unused-vars) — Risk: 30/100
packages/runtime-core/src/redact.ts:12: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:12: packages/runtime-core/src/redact.ts:12:6: Type alias 'AttrValue' is declared but never used.
Flagged by Autter security & observability checks.
| options?: boolean | RedactOptions, | ||
| ): (attributes?: Attributes | null) => Attributes { | ||
| if (options === false) { | ||
| return (attributes) => ({ ...(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:405:29: 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:405: packages/runtime-core/src/redact.ts:405:29: Empty fallbacks in spreads are unnecessary
Flagged by Autter security & observability checks.
| "node": ">=20" | ||
| } | ||
| }, | ||
| "examples/edge-worker": { |
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.
| @@ -1,5 +1,41 @@ | |||
| # Changelog | |||
|
|
|||
| ## [1.5.0] - 2026-10-08 | |||
There was a problem hiding this comment.
🟠 [deterministic] Possible PII in source: PHONE_NUMBER — Risk: 72/100
Possible PHONE_NUMBER 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 PHONE_NUMBER value at CHANGELOG.md:3. 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.error.fix", "Email jane@example.com for help"], | ||
| ["autter.error.link", "https://user:pw@docs.example.com/payments?token=1#declined"], | ||
| ["autter.error.expected", "true"], | ||
| ["autter.request.id", "req_0123456789"], |
There was a problem hiding this comment.
🟠 [deterministic] Possible PII in source: PHONE_NUMBER — Risk: 72/100
Possible PHONE_NUMBER 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 PHONE_NUMBER value at packages/otlp-ingester/src/coded-errors.test.ts:114. 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.
| * | ||
| * `event.waitUntil` (NextFetchEvent) is used for delivery automatically. | ||
| */ | ||
| export * from "@autter/runtime-edge"; |
There was a problem hiding this comment.
🟡 [deterministic] Build failed: @autter/runtime-next — Risk: 40/100
npm run build could not verify the @autter/runtime-next build in packages/runtime-next — this is not evidence that this PR breaks it.
What happened: The build for @autter/runtime-next fails because TypeScript cannot resolve modules @autter/runtime-edge and @autter/runtime-node in src/edge.ts and src/server.ts.
How to fix: This looks like an incomplete install or missing lockfile. Try running npm install across the workspace or build the dependent packages before attempting to build runtime-next.
Build output
Cannot find module '@autter/runtime-edge' or its corresponding type declarations.
Error: error occurred in dts build
at Worker.<anonymous> (/tmp/autter-agentic-PpMD8i/node_modules/tsup/dist/index.js:1545:26)
at Worker.emit (node:events:514:28)
at MessagePort.<anonymous> (node:internal/worker:350:14)
at [nodejs.internal.kHybridDispatch] (node:internal/event_target:848:20)
at MessagePort.<anonymous> (node:internal/per_context/messageport:23:28)
src/server.ts(47,8): error TS2307: Cannot find module '@autter/runtime-node' or its corresponding type declarations.
src/server.ts(92,8): error TS2307: Cannot find module '@autter/runtime-node' or its corresponding type declarations.
src/server.ts(130,8): error TS2307: Cannot find module '@autter/runtime-node' or its corresponding type declarations.
npm error Lifecycle script `build` failed with error:
npm error code 1
npm error path /tmp/autter-agentic-PpMD8i/packages/runtime-next
npm error workspace @autter/runtime-next@1.5.0
npm error location /tmp/autter-agentic-PpMD8i/packages/runtime-next
npm error command failed
npm error command sh -c node -e "require('node:fs').rmSync('dist',{recursive:true,force:true})" && tsup
🛠 AI fix prompt (copy & paste into your coding agent)
The @autter/runtime-next build (`npm run build` in `packages/runtime-next`) fails. The build for @autter/runtime-next fails because TypeScript cannot resolve modules @autter/runtime-edge and @autter/runtime-node in src/edge.ts and src/server.ts. Fix: This looks like an incomplete install or missing lockfile. Try running npm install across the workspace or build the dependent packages before attempting to build runtime-next.
Flagged by Autter security & observability checks.
| * Works with or without initAutterServer/initAutterLogging; before init it | ||
| * replaces the default console output instead of adding to it. | ||
| */ | ||
| import { isSummary, type RuntimeEvent, type RuntimeOutcome } from "@autter/runtime-core"; |
There was a problem hiding this comment.
🟡 [deterministic] Build failed: @autter/runtime-node — Risk: 40/100
npm run build could not verify the @autter/runtime-node build in packages/runtime-node — this is not evidence that this PR breaks it.
What happened: The build for @autter/runtime-node fails because it cannot resolve @autter/runtime-core, specifically reporting that ./dist/index.js is missing on the file system.
How to fix: This looks like an incomplete install or missing lockfile. Ensure @autter/runtime-core is built and available in node_modules, or run npm install to refresh the workspace dependencies.
Build output
mark the path "@autter/runtime-core" as external to exclude it from the bundle, which will remove this error and leave the unresolved path in the bundle.
✘ [ERROR] Could not resolve "@autter/runtime-core"
src/logging-only.ts:7:32:
7 │ import { errorAttributes } from "@autter/runtime-core";
╵ ~~~~~~~~~~~~~~~~~~~~~~
The module "./dist/index.js" was not found on the file system:
../../node_modules/@autter/runtime-core/package.json:14:13:
14 │ "import": "./dist/index.js"
╵ ~~~~~~~~~~~~~~~~~
You can mark the path "@autter/runtime-core" as external to exclude it from the bundle, which will remove this error and leave the unresolved path in the bundle.
✘ [ERROR] Could not resolve "@autter/runtime-core"
src/redact.ts:11:7:
11 │ } from "@autter/runtime-core";
╵ ~~~~~~~~~~~~~~~~~~~~~~
The module "./dist/index.js" was not found on the file system:
../../node_modules/@autter/runtime-core/package.json:14:13:
14 │ "import": "./dist/index.js"
╵ ~~~~~~~~~~~~~~~~~
You can mark the path "@autter/runtime-core" as external to e…
🛠 AI fix prompt (copy & paste into your coding agent)
The @autter/runtime-node build (`npm run build` in `packages/runtime-node`) fails. The build for @autter/runtime-node fails because it cannot resolve @autter/runtime-core, specifically reporting that ./dist/index.js is missing on the file system. Fix: This looks like an incomplete install or missing lockfile. Ensure @autter/runtime-core is built and available in node_modules, or run npm install to refresh the workspace dependencies.
Flagged by Autter security & observability checks.
| // reach grouping through the trace path. The exception: logger-only / | ||
| // edge SDKs mark span-less error records `autter.capture.mode = "log"`, | ||
| // and those are promoted to occurrences here — see log-promotion.ts. | ||
| const promoted = await promoteLogs(ctx, logs); |
There was a problem hiding this comment.
🟠 [ai] Log promotion adds an expensive scan without a stricter route limit — Risk: 50/100
POST /v1/logs still requires a valid server ingest key and is protected by the shared per-key limit, but this change adds a ClickHouse DISTINCT trace lookup and occurrence/metric writes to that endpoint. The only server-key budget is the generic 300 requests/minute default; each request may be up to 1 MiB and normalize up to 2,000 records. For promotion candidates, the lookup range is derived from the submitted event timestamps and can span the stored occurrence interval, while the query filters by trace IDs rather than a dedicated trace-ID ordering key. A caller with a valid key can therefore amplify load on the shared ClickHouse store with many large batches and wide lookup ranges. Add a substantially tighter per-key budget for this expensive promotion path and bound the permitted timestamp span/query work.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/otlp-ingester/src/server.ts,packages/otlp-ingester/src/config.ts,packages/otlp-ingester/src/logs.ts,packages/otlp-ingester/src/log-promotion.ts,packages/otlp-ingester/src/clickhouse.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Add a dedicated, substantially tighter per-key rate limit for /v1/logs promotion work, and constrain the dedupe lookup to a bounded event-time window (reject, clamp, or skip lookup for batches whose candidate timestamps span too far). Preserve the existing ingest-key authentication and tenant scoping.
Flagged by Autter security & observability checks.
| const body = JSON.stringify( | ||
| buildOtlpLogsRequest( | ||
| { | ||
| service: opts.service, |
There was a problem hiding this comment.
🟠 [ai] Per-isolate queue can send records using another request's credentials — Risk: 50/100
The exporter queue is shared across requests, but queued entries contain only encoded records; delivery builds the resource and authorization header from the options of whichever request happens to flush the queue. With the supported env-dependent options callback, request A can enqueue records while its options have no apiKey (delivery returns early at line 201), then request B can flush those records using B's service/environment and apiKey. This can export records that were configured not to export and, when keys select different tenants, store A's telemetry under B's tenant. Keep queued records partitioned/tagged by their original destination configuration, or discard records when export is disabled.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/runtime-edge/src/index.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Preserve the resolved destination configuration with each queued record/batch and only send records using that configuration; alternatively do not queue records when apiKey is absent. Add coverage for two sequential requests with differing env-derived service/apiKey settings.
Flagged by Autter security & observability checks.
| const out: Record<string, unknown> = {}; | ||
| if (!error || typeof error !== "object") return out; | ||
| const e = error as Record<string, unknown>; | ||
| if (typeof e.code === "string" && e.code.length <= 80 && CODE_RE.test(e.code)) { |
There was a problem hiding this comment.
🟡 [ai] Throwing error metadata getters abort browser exception capture — Risk: 40/100
The new errorContext path directly reads duck-typed fields such as code, why, fix, link, expected, and requestId without guarding property access. An Error subclass or Proxy whose code getter throws therefore causes captureException to throw at this line before enqueue(event, true), so callers do not report the original exception; the changed React boundary calls this public API directly from componentDidCatch, and the global error/rejection handlers also call errorContext before enqueueing. This removes the prior guarantee that captureException could report an Error without inspecting optional metadata.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/runtime-next/src/error-boundary.ts,packages/runtime-browser/src/index.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Read each optional metadata field through a guarded helper (or wrap the metadata extraction as a whole) so throwing getters are ignored and captureException always reaches enqueue(event, true). Apply the same fail-open behavior to global error and unhandled-rejection listeners.
Flagged by Autter security & observability checks.
| // of the value survives — useful context like a stack frame stays readable). | ||
| const PRIVATE_KEY_BLOCK_RE = | ||
| /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g; | ||
| const EMAIL_VALUE_RE = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi; |
There was a problem hiding this comment.
🟠 [ai] ReDoS in shared value-redaction regexes (email and URL-credential scrubbers) blocks the event loop on attacker-controlled log, error and context strings — Risk: 55/100
The shared redaction module applies an unbounded email regex and an unbounded URL-credential regex to every string that flows into request summaries, log records, captured exceptions and operation context. A string of tens of kilobytes with no @ makes these regexes take seconds of synchronous CPU time. A single request can therefore freeze the Node process or edge isolate, and the stall blocks all other requests.
redactString() runs EMAIL_VALUE_RE and URL_CREDENTIALS_RE over every string value. boundContext() and mergeContext() call it before the 16 KB truncation, so the full-length input is scanned.
const EMAIL_VALUE_RE = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi;
...
const URL_CREDENTIALS_RE =
/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/:@]+:[^\s/@]+@/gi;The email pattern uses an unbounded leading class [A-Z0-9._%+-]+. For each start offset the engine consumes the whole run of local-part characters and then fails to find @, which gives O(n²) behaviour on inputs such as "a".repeat(n) or "a.".repeat(n). The URL pattern has the same shape: an unbounded scheme [a-z0-9+.-]* and unbounded userinfo classes, so inputs like "xox" + "b-".repeat(n) backtrack heavily.
Reachability in the new code: the new request wide-event path (autterRequests, withRuntimeRequest, autterFastify, withAutter) routes runtimeContext.set, inline logs, error messages (captureException, exception.message, message) and operation names through boundContext and makeRedactor. These calls run synchronously on the request path, in emit() in logger.ts and in record() in the edge buildRuntime. The edge SDK also invokes the redactor on the thrown error's message.
- Bundle runtime-core, or run any app using autterRequests with
runtimeContext.set({ search: req.body }). - Send a POST with a 50,000-character body of the letter
a. - Concurrently poll an unrelated endpoint and observe that its latency equals the redaction time (about 4 s).
- Alternatively, call
boundContext({m: "a".repeat(50000)}, makeRedactor(true))directly and time it.
Evidence:
Measured on Node 24 with the repo sources bundled:
50 KB body POST /search -> attack request 4162 ms
unrelated GET /health2 during attack -> max latency 4161 ms
edge withAutter throw with 20 KB message -> 3861 ms
Old EMAIL_VALUE_RE on 40 KB 'a'*n -> 1867 ms (n=20000: 464 ms, ~4x per doubling = quadratic)
Old URL_CREDENTIALS_RE on 'xox'+'b-'*25000 -> ~2.3 s
After fix: 50 KB 'a' -> 30 ms, 'a.'*25000 -> 35 ms
The MAX_REDACTION_WORK and depth limits bound the number of nodes, but not the length of a single string. The 16 KB cap in boundContext is applied after redaction. The framework body-size limit is the only mitigating factor; Express text/json defaults allow 100 KB, enough for a ~12 s stall. The same regexes existed in runtime-node 1.4.0, but the diff moves them to shared core and newly routes request summaries, error messages and edge handler errors through them on the request path. No timeout or length pre-check was found on this path.
Proof of concept:
// Express + autterRequests (built from the repo sources)
app.use(autterRequests());
app.use(express.text({ limit: "1mb" }));
app.post("/search", (req, res) => { runtimeContext.set({ search: req.body }); res.send("ok"); });
app.get("/health2", (q, s) => s.send("hi"));
// attacker:
await fetch(base + "/search", { method: "POST", headers: {"content-type": "text/plain"}, body: "a".repeat(50000) });
// victim poll during the attack
// Result: attack request 4162 ms; unrelated GET /health2 max latency 4161 ms
// Edge:
const h = withAutter({service:"x"}, async (req) => { throw new Error("bad input: " + await req.text()); });
await h(new Request("http://x/y", {method:"POST", body:"a".repeat(20000)})).catch(()=>{}); // ~3.9 s🛠 AI fix prompt (copy & paste into your coding agent)
Bound the quantifiers in the email and URL-credential patterns so matching is linear (local part 64, domain 255, scheme 32, userinfo 256). Truncate each string to a fixed maximum length before running value patterns. Apply the same bounds to the equivalent email scrubbing patterns in the ingester (`context.ts`, `normalize-browser.ts`, `error-fields.ts`), where an attacker holding a public client key can send 20-40 KB strings to `/v1/browser` and block the ingester event loop for about 0.6-1.9 s per request (health checks were delayed too). Add a regression test that redacts a 100 KB string of `a` and of `a.` repeated within a small time budget.
Impact to account for:
An unauthenticated remote attacker can stall the whole server process. In the test, one 50 KB request body took about 4.2 s of synchronous CPU, and an unrelated `GET /health2` issued during that window was delayed 4.16 s. Cost grows quadratically: 100 KB takes roughly 12 s and 200 KB roughly 48 s. Concurrent requests give sustained denial of service. The same input also stalls the edge SDK. A 20 KB attacker-controlled error message took 3.9 s in `withAutter`, which can exhaust Worker CPU limits. The precondition is that the application puts an attacker-influenced string into runtimeContext/runtimeLogger/captureException attributes, an error message, or an operation name. This is common (search terms, validation errors that echo input, usernames).
Flagged by Autter security & observability checks.
🧪 Autter test runAutter checked This PR changes 80 source files. It ran the project's test command for the workspace this PR touched, because those suites are the fastest check that the change did not break existing behavior. It wrote 3 temporary tests for changed files that had no matching test, so the new behavior is still checked. It also executed 3 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: 16 checks executed · 14 passed · 2 failed. Project test commands
Autter targeted verification11 tests executed · 10 passed · 1 failed. Critical feature verificationCould not run: no valid structured result (exit=0, hadFile=true); Code-level simulationsRevision
Declared tests: 30 test file(s) found — 23 ran, 0 not observed in suite output, 7 did not run. The project's command reported 10 of them. Autter ran the other 13 files one by one so every declared test has a verdict (13 passed, 0 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 |
|---|---|---|
examples/edge-worker/src/index.js |
— | ✅ no declared test — temporary test passed |
examples/express-app/server.js |
— | ✅ no declared test — temporary test passed |
packages/otlp-ingester/src/context.ts |
packages/otlp-ingester/src/coded-errors.test.ts |
✅ existing test passes |
packages/otlp-ingester/src/error-fields.ts |
packages/otlp-ingester/src/coded-errors.test.ts |
✅ existing test passes |
packages/otlp-ingester/src/clickhouse.ts |
packages/otlp-ingester/src/coded-errors.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/fingerprint.ts |
packages/otlp-ingester/src/coded-errors.test.ts |
✅ existing test passes |
packages/otlp-ingester/src/log-promotion.ts |
packages/otlp-ingester/src/coded-errors.test.ts |
✅ existing test passes |
🤖 Coverage-check evidence
examples/edge-worker/src/index.js
examples/express-app/server.js
packages/otlp-ingester/src/context.ts
packages/otlp-ingester/src/error-fields.ts
packages/otlp-ingester/src/clickhouse.ts
packages/otlp-ingester/src/config.ts
packages/otlp-ingester/src/fingerprint.ts
packages/otlp-ingester/src/log-promotion.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 ci && npm run build, thennpm test, and confirm theruntime-nextdts build resolves@autter/runtime-edge(exercisesscripts/ensure-built.mjs). — verified by agent execution - ✅ Run
npm run size -w @autter/runtime-browserand confirm the bundle is within the 5 KB brotlied budget. — verified by agent execution - ⬜ Start local ClickHouse and the ingester (Docker Compose, ports 8123 and 4318). Confirm migrations apply twice without error and that
runtime_occurrenceshas the new code columns and request-id index, andruntime_logshas its TTL. — Run is unit tests, not full Docker Compose setup. - ⬜ POST a
/v1/browserbeacon withautter.error.code: "payments.card_declined",autter.error.why/fix/linkandautter.request.id. Read back from ClickHouse and confirmfingerprintScheme = "code-v1"and the lifted fields. — Run does not cover POST /v1/browser beacon E2E. - ⬜ Repeat the beacon with a different message and route but the same code, and confirm the same fingerprint. — Run does not cover repeating beacons to confirm fingerprint.
- ⬜ Send two beacons with id-shaped codes (
req_8f3a1c4d,req_9b2e77aa) and identical stacks. Confirm they do not create separatecode-v1issues. — Run does not cover id-shaped code beacon logic. - ⬜ Send a beacon with
autter.error.why: "declined for jane@acme.com"and confirm the stored and sink-forwardedwhyhas the email masked, consistent withattributes.context. — Run does not cover specific email masking test. - ⬜ Send an uncoded error and confirm its fingerprint equals the one produced on
main(message-v1, unchanged). — Run does not cover uncoded error fingerprint comparison. - ❌ Configure
AUTTER_SINK_URLto a local receiver and inspect the webhook body. ConfirmtraceId,route,methodandstatusCodeare present (includingnulland0) and the new fields appear only when set. — agent execution observed the wrong behavior - ⬜ Run
examples/express-appand hit/boom,/api/okandPOST /api/checkout. Confirm request summaries are emitted and linked to occurrences viaautter.request.id. — Run does not cover examples/express-app tests. - ⬜ Run
examples/edge-workerwithwrangler devand confirm a request summary and a coded error reach the ingester. — Run does not cover examples/edge-worker tests. - ⬜ In a browser test page, throw an
Errorsubclass whosecodegetter throws and confirmcaptureExceptionstill enqueues the original exception. — Run does not cover browser test page steps.
🤖 Agent-executed checks
✅ Run npm ci && npm run build, then npm test, and confirm the runtime-next dts build resolves @autter/runtime-edge (exercises scripts/ensure-built.mjs).
npm ci succeeded. All workspace builds passed, including runtime-next DTS declarations for @autter/runtime-edge. npm test passed: runtime-core 15, runtime-node 55 (1 skipped), runtime-edge 7, runtime…
✅ Run npm run size -w @autter/runtime-browser and confirm the bundle is within the 5 KB brotlied budget.
size-limit: 3.92 kB minified and brotlied; configured limit 5 kB.
❌ Configure AUTTER_SINK_URL to a local receiver and inspect the webhook body. Confirm traceId, route, method and statusCode are present (including null and 0) and the new fields appear only when set.
The receiver got statusCode:0 and set errorCode/why/requestId fields, but null traceId, route, and method were omitted (observed as undefined). The assertion requiring those keys with null values fai…
⬜ items could not be verified automatically and still need a manual check.
|
🟡 [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. |
| @@ -0,0 +1,627 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: @autter/otlp-ingester — Risk: 40/100
npm run test could not verify the @autter/otlp-ingester test suite in packages/otlp-ingester — this is not evidence that this PR breaks it.
What happened: The test suite failed because the required packages @clickhouse/client and zod are not found during import in the otlp-ingester package.
How to fix: This looks like an incomplete install or missing lockfile issue. Please run npm install to ensure all dependencies are properly installed before running tests again.
Test output
ckageName, fileURLToPath(base), null);
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'zod' imported from /tmp/autter-agentic-PpMD8i/packages/otlp-ingester/src/normalize-browser.ts
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 nextStep (node:internal/modules/customization_hooks:189:26)
at resolveBaseSync (file:///tmp/autter-agentic-PpMD8i/node_modules/tsx/dist/register-zZ7SWseA.mjs:2:9102)
at resolveDirectorySync (file:///tmp/autter-agentic-PpMD8i/node_modules/tsx/dist/register-zZ7SWseA.mjs:2:10315)
at resolveTsPathsSync (file:///tmp/autter-agentic-PpMD8i/node_modules/tsx/dist/register-zZ7SWseA.mjs:2:11547) {
code: 'ERR_MODULE_NOT_FOUND'
}
Node.js v24.21.0
✖ src/continuous-detection.test.ts (391.735303ms)
✔ occurrenceIdFor is a pure function of the signal (2.548885m…
🛠 AI fix prompt (copy & paste into your coding agent)
The @autter/otlp-ingester test suite (`npm run test` in `packages/otlp-ingester`) fails. The test suite failed because the required packages @clickhouse/client and zod are not found during import in the otlp-ingester package. Fix: This looks like an incomplete install or missing lockfile issue. Please run npm install to ensure all dependencies are properly installed before running tests again.
Flagged by Autter security & observability checks.
| @@ -0,0 +1,233 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-core/test/core.test.mjs — Risk: 40/100
packages/runtime-core/test/core.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/core.test.mjs'.
Failing tests
- the file failed before any test reported — The test expects /tmp/autter-agentic-PpMD8i/packages/runtime-core/dist/index.js to exist, but Node.js reports it cannot find the module. The observed value is an ERR_MODULE_NOT_FOUND error confirming the build artifact is missing.
node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-core/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-core/test/core.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
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)
at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///tmp/autter-agentic-PpMD8i/packages/runtime-core/dist/index.js'
}
Node.js v24.21.0
✖ test/core.test.mjs (50.678393ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
…
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-core/test/core.test.mjs fails when run with `node --test 'test/core.test.mjs'`: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-core/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-core/test/core.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
| @@ -0,0 +1,143 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-edge/test/edge.test.mjs — Risk: 40/100
packages/runtime-edge/test/edge.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/edge.test.mjs'.
Failing tests
- the file failed before any test reported — The test expects /tmp/autter-agentic-PpMD8i/packages/runtime-edge/dist/index.js to exist, but Node.js reports it cannot find the module. The observed value is an ERR_MODULE_NOT_FOUND error confirming the build artifact is missing.
node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-edge/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-edge/test/edge.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
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)
at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///tmp/autter-agentic-PpMD8i/packages/runtime-edge/dist/index.js'
}
Node.js v24.21.0
✖ test/edge.test.mjs (51.798182ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
…
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-edge/test/edge.test.mjs fails when run with `node --test 'test/edge.test.mjs'`: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-edge/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-edge/test/edge.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
| @@ -0,0 +1,65 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-next/test/next.test.mjs — Risk: 40/100
packages/runtime-next/test/next.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/next.test.mjs'.
Failing tests
- the file failed before any test reported — The test expects /tmp/autter-agentic-PpMD8i/node_modules/@autter/runtime-node/dist/index.js to exist, but Node.js reports it cannot find the module. The observed value is an ERR_MODULE_NOT_FOUND error confirming the dependency build artifact is missing.
node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/node_modules/@autter/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-next/dist/chunk-LSSGZZDH.js
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
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)
at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///tmp/autter-agentic-PpMD8i/node_modules/@autter/runtime-node/dist/index.js'
}
Node.js v24.21.0
✖ test/next.test.mjs (47.514914ms)
ℹ tests 1
ℹ…
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-next/test/next.test.mjs fails when run with `node --test 'test/next.test.mjs'`: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/node_modules/@autter/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-next/dist/chunk-LSSGZZDH.js
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:inter. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
| @@ -0,0 +1,34 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-node/test/golden.test.mjs — Risk: 40/100
packages/runtime-node/test/golden.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/golden.test.mjs'.
Failing tests
- the file failed before any test reported — The test expects /tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js to exist, but Node.js reports it cannot find the module. The observed value is an ERR_MODULE_NOT_FOUND error confirming the build artifact is missing.
node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/golden.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
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)
at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js'
}
Node.js v24.21.0
✖ test/golden.test.mjs (47.020098ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fai…
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-node/test/golden.test.mjs fails when run with `node --test 'test/golden.test.mjs'`: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/golden.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/es. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
| @@ -0,0 +1,53 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-node/test/hook-mode.test.mjs — Risk: 40/100
packages/runtime-node/test/hook-mode.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/hook-mode.test.mjs'.
Failing tests
- the file failed before any test reported — The test expects /tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js to exist, but Node.js reports it cannot find the module. The observed value is an ERR_MODULE_NOT_FOUND error confirming the build artifact is missing.
node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/hook-mode.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
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)
at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js'
}
Node.js v24.21.0
✖ test/hook-mode.test.mjs (77.730288ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0…
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-node/test/hook-mode.test.mjs fails when run with `node --test 'test/hook-mode.test.mjs'`: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/hook-mode.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
| @@ -0,0 +1,68 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-node/test/logging-only.test.mjs — Risk: 40/100
packages/runtime-node/test/logging-only.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/logging-only.test.mjs'.
Failing tests
- the file failed before any test reported — The test expects /tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js to exist, but Node.js reports it cannot find the module. The observed value is an ERR_MODULE_NOT_FOUND error confirming the build artifact is missing.
node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/logging-only.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
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)
at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js'
}
Node.js v24.21.0
✖ test/logging-only.test.mjs (70.975485ms)
ℹ tests 1
ℹ suites 0
ℹ …
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-node/test/logging-only.test.mjs fails when run with `node --test 'test/logging-only.test.mjs'`: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/logging-only.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modu. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
| @@ -0,0 +1,217 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-node/test/operations.test.mjs — Risk: 40/100
packages/runtime-node/test/operations.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/operations.test.mjs'.
Failing tests
- the file failed before any test reported — The test expects /tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js to exist, but Node.js reports it cannot find the module. The observed value is an ERR_MODULE_NOT_FOUND error confirming the build artifact is missing.
node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/operations.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
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)
at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js'
}
Node.js v24.21.0
✖ test/operations.test.mjs (72.880891ms)
ℹ tests 1
ℹ suites 0
ℹ pass…
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-node/test/operations.test.mjs fails when run with `node --test 'test/operations.test.mjs'`: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/operations.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/module. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
| @@ -0,0 +1,308 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-node/test/requests.test.mjs — Risk: 40/100
packages/runtime-node/test/requests.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/requests.test.mjs'.
Failing tests
- the file failed before any test reported — The test expects /tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js to exist, but Node.js reports it cannot find the module. The observed value is an ERR_MODULE_NOT_FOUND error confirming the build artifact is missing.
node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/requests.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
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)
at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js'
}
Node.js v24.21.0
✖ test/requests.test.mjs (78.706788ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ…
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-node/test/requests.test.mjs fails when run with `node --test 'test/requests.test.mjs'`: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/requests.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
| @@ -0,0 +1,89 @@ | |||
| import assert from "node:assert/strict"; | |||
There was a problem hiding this comment.
🟡 [deterministic] Tests failed: packages/runtime-node/test/sinks.test.mjs — Risk: 40/100
packages/runtime-node/test/sinks.test.mjs fails. The project's own test command never reported this file, so Autter ran it on its own with node --test 'test/sinks.test.mjs'.
Failing tests
- the file failed before any test reported — The test expects /tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js to exist, but Node.js reports it cannot find the module. The observed value is an ERR_MODULE_NOT_FOUND error confirming the build artifact is missing.
node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/sinks.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
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)
at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js'
}
Node.js v24.21.0
✖ test/sinks.test.mjs (49.09521ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1…
🛠 AI fix prompt (copy & paste into your coding agent)
The test file packages/runtime-node/test/sinks.test.mjs fails when run with `node --test 'test/sinks.test.mjs'`: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/tmp/autter-agentic-PpMD8i/packages/runtime-node/dist/index.js' imported from /tmp/autter-agentic-PpMD8i/packages/runtime-node/test/sinks.test.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm. Fix the code (or the test if it is genuinely stale) so it passes.
Flagged by Autter security & observability checks.
Autter product walkAutter did not drive this change in a browser. This PR changes a backend/API, but Autter could not find a |
|
🟡 [deterministic] Product walk failed: npm test — Risk: 40/100 Planned command The PR touches OTLP ingestion (sink.ts/server.ts), core fingerprinting/redaction/error-code logic, and versioned golden fixtures — all library packages with no UI/server start. There is no start command, so HTTP steps are impossible. The right adversarial exercise is the targeted package test suites: OTLP ingester tests stress duplicate/concurrent batch delivery and the sink's idempotency/partial- Expected: All package suites pass: runtime-core exercises fingerprint-code boundary inputs (empty/unicode/oversized) and redact rules; runtime-node replays the golden-1.4.0-logs fixture proving schema_version_s 1 attempt(s), 0 of 3 retries used. src/edge.ts(14,15): error TS2307: Cannot find module '@autter/runtime-edge' or its corresponding type declarations. Error: error occurred in dts build npm error Lifecycle script 🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
Autter review completed — merge blockedAutter completed its review and found at least one confirmed merge-blocking finding. Autter blocked this PR: test; test_failure; 1 confirmed correctness/runtime finding(s). See the findings below and the full PR review for details. Other findings explain the additional warnings, but they did not independently block this PR. The merge gate remains blocked until the confirmed finding is resolved. RetryRetrying reruns the review on the same commit. If the commit is unchanged, the same finding and block may be reproduced.
Autter will post a new comment when the retry starts. |
- 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>
|
Pushed 4489bcd addressing the review:
Not changed (false positives):
@autter review |
|
🔄 Re-running my review on the latest commit — fresh findings will appear shortly. 🤖 Replying to your mention — mention @autter again to continue. |
|
🔄 Retrying the PR review via |
| // Behaviour is unchanged for real secrets. | ||
| const out = redact({ | ||
| mail: "contact jane.doe+x@example.co.uk now", | ||
| db: "postgres://user:pw@host/db", |
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 /checkout, GET /healthz, GET /api/users/:id, POST /api/checkout, POST /api/users/:id, GET /boom; dependent files packages/otlp-ingester/src/fingerprint.ts, packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/sink.ts, packages/otlp-ingester/src/server.sink.test.ts, packages/otlp-ingester/src/sink.test.ts, packages/otlp-ingester/src/llm.ts, packages/otlp-ingester/src/index.ts, packages/otlp-ingester/src/auth.ts.
References:
- https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html — Cloud-native secret managers often provide built-in support for automated rotation using serverless functions (e.g., AWS Lambda, Azure Functions).
- https://docs.aws.amazon.com/pdfs/wellarchitected/latest/security-pillar/wellarchitected-security-pillar.pdf — For access to databases and third-party tools, we recommend storing access credentials in AWS Secrets Manager and granting access to the ...
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- API endpoints:
POST /checkout,GET /healthz,GET /api/users/:id,POST /api/checkout,POST /api/users/:id,GET /boom - Dependent files:
packages/otlp-ingester/src/fingerprint.ts,packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts,packages/otlp-ingester/src/llm.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts
🛠 AI fix prompt (copy & paste into your coding agent)
In `packages/runtime-core/test/core.test.mjs` around line 255 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 /checkout`, `GET /healthz`, `GET /api/users/:id`, `POST /api/checkout`, `POST /api/users/:id`, `GET /boom`; dependent files `packages/otlp-ingester/src/fingerprint.ts`, `packages/otlp-ingester/src/normalize-otlp.ts`, `packages/otlp-ingester/src/sink.ts`, `packages/otlp-ingester/src/server.sink.test.ts`, `packages/otlp-ingester/src/sink.test.ts`, `packages/otlp-ingester/src/llm.ts`, `packages/otlp-ingester/src/index.ts`, `packages/otlp-ingester/src/auth.ts`..
Flagged by Autter security & observability checks.
🤖 Release Notes CuratorImpact: Changelog entry (ready to paste): ## [1.5.0] - 2026-10-08
Packages: `otlp-ingester` 1.5.0, `runtime-node` 1.5.0, `runtime-next` 1.5.0,
`runtime-browser` 1.4.0, new `runtime-edge` 1.0.0.
Upgrade the ingester **first**; everything below is additive — 1.4.0 code keeps
working unchanged.
### Features
- **Request summaries** — one record per HTTP request (route template, status,
duration, outcome, request id, context, inline messages), always kept, with
`x-request-id` honoured, echoed and exposed to CORS. Express/Connect
(`autterRequests`), Fastify (`autterFastify`), fetch-style handlers and Next.js
route handlers (`withRuntimeRequest`).
- **`runtimeContext`** — read and set context, outcomes, ids and inline
debug/info messages from anywhere inside a request or operation; debug/info
messages fold into the summary instead of being dropped.
- **Coded errors** — `defineRuntimeErrors` / `RuntimeError` give errors a stable
`code`, plus optional `why`, `fix`, docs link and `expected` flag.
`captureException` reads them from any error object, and errors sharing a code
are grouped into a single issue regardless of message or stack. `expected`
failures (declines, validation, quota) are recorded but never open incidents.
- **Client-safe error responses** — `autterErrorResponse` / `toClientError`
return `{ error: { message, code, fix, requestId } }` without leaking
internals.
- **Beyond the request** — `fork`, `runInBackground`, cross-process carriers with
OpenTelemetry span links, `waitUntil`, and per-operation AI usage rollup.
- **Logger-only mode** — `initAutterLogging` adds requests, operations and coded
errors to apps that already run their own OpenTelemetry SDK, without a second
NodeSDK.
- **New `@autter/runtime-edge` 1.0.0** — request summaries, request ids and coded
errors for Cloudflare Workers, Vercel Edge, Deno and Bun with zero
dependencies; also available as `@autter/runtime-next/edge`.
- **Developer ergonomics** — enrichers, `otlp`/`console`/`file` sinks (records
written to `.autter/runtime/*.jsonl` in development) and a
`@autter/runtime-node/testing` subpath.
- **Self-hosted ingester** — stores the new request and error fields, feeds a new
per-route request rollup, applies a configurable `LOG_TTL_DAYS` log retention
(default 14 days) and promotes log-captured exceptions to issues without
double-counting traced ones.(Heads-up: the PR already edits Custom agent · runs after review · configured in Autter |
| const lookups: PromotionLookup[] = []; | ||
| let start = 0; | ||
| while (start < eligible.length && lookups.length < MAX_LOOKUP_WINDOWS) { | ||
| const newest = eligible[start]!.at; |
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/log-promotion.ts:88: Forbidden non-null assertion.
Flagged by Autter security & observability checks.
| if (!exporter.queue.length) return; | ||
| exporter.flushing = (async () => { | ||
| while (exporter.queue.length) { | ||
| const destination = exporter.queue[0]!.destination; |
There was a problem hiding this comment.
🟠 [deterministic] Biome: lint/style/noNonNullAssertion — Risk: 55/100
Forbidden non-null assertion.
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the Biome `lint/style/noNonNullAssertion` issue at packages/runtime-edge/src/index.ts:243: Forbidden non-null assertion.
Flagged by Autter security & observability checks.
| const bodies = []; | ||
| const collector = 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/runtime-node/test/fixtures/golden-scenario.mjs:23: The assignment should not be in an expression.
Flagged by Autter security & observability checks.
| const requests = []; | ||
| const collector = 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/runtime-node/test/logging-only.test.mjs:17: The assignment should not be in an expression.
Flagged by Autter security & observability checks.
| promotionLookups, | ||
| } from "./log-promotion.js"; | ||
| import { sanitizeRuntimeContext } from "./context.js"; | ||
| import { sinkOccurrence } from "./sink.js"; |
There was a problem hiding this comment.
🟡 [deterministic] oxlint: oxlint — Risk: 45/100
packages/otlp-ingester/src/coded-errors.test.ts:26:10: Identifier sinkOccurrence has already been declared
🛠 AI fix prompt (copy & paste into your coding agent)
Fix the oxlint `oxlint` issue at packages/otlp-ingester/src/coded-errors.test.ts:26: packages/otlp-ingester/src/coded-errors.test.ts:26:10: Identifier `sinkOccurrence` has already been declared
Flagged by Autter security & observability checks.
| .replace(/(?<![A-Z0-9._%+-])[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, "[redacted]") | ||
| .replace( | ||
| /\b(?:bearer\s+[A-Za-z0-9._~+\/=~-]{10,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+|gh[pousr]_[A-Za-z0-9]{20,}|sk-[A-Za-z0-9_-]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|autter_(?:rt|pat)_[A-Za-z0-9_-]{10,}|(?:AKIA|ASIA)[A-Z0-9]{16})\b/gi, | ||
| /\b(?:bearer\s+[A-Za-z0-9._~+\/=~-]{10,}|(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+|gh[pousr]_[A-Za-z0-9]{20,}|sk-[A-Za-z0-9_-]{20,}|(?<![A-Za-z0-9-])xox[baprs]-[A-Za-z0-9-]{10,}|(?<![A-Za-z0-9_-])autter_(?:rt|pat)_[A-Za-z0-9_-]{10,}|(?:AKIA|ASIA)[A-Z0-9]{16})\b/gi, |
There was a problem hiding this comment.
🟡 [deterministic] oxlint: eslint(no-useless-escape) — Risk: 30/100
packages/otlp-ingester/src/context.ts:11:34: 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/context.ts:11: packages/otlp-ingester/src/context.ts:11:34: Unnecessary escape character '/'
Flagged by Autter security & observability checks.
There was a problem hiding this comment.
Autter could not complete PR review for #25. Autter hit an internal error and could not complete this review. Push a new revision to re-run it.
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>
…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>
🤖 AI authorship54% of changed code was AI-written — 0% human, 46% untracked, across 10,010 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. |
| } { | ||
| const code = validErrorCode(input.errorCode); | ||
| return code | ||
| ? { fingerprint: codeFingerprint(input.service, code), scheme: "code-v1" } |
There was a problem hiding this comment.
🟠 [ai] Declaring an error code moves occurrences to a new code-v1 issue; the 'never regroup' comment is inaccurate — Risk: 50/100
For errors without a valid code, message-v1 hashing is unchanged. But once an occurrence carries a valid autter.error.code, occurrenceFingerprint returns a code-v1 fingerprint built from service + code only. New occurrences of that error then group into a different issue, and the prior message-v1 issue stops receiving them.
The doc comment's claim that existing issues never regroup is inaccurate. It should state that adopting a code re-groups the error. Since the repo treats fingerprinting changes as breaking, this behaviour should be documented and discussed explicitly.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Functions/symbols:
normalizeRoute,fingerprintOccurrence,deriveFields,occurrenceIdFor,normalizeTraces,normalizeMetrics,instrumentLlmClient,loadConfig - API endpoints:
POST /checkout,GET /healthz,GET /api/users/:id,POST /api/checkout,POST /api/users/:id,GET /boom - Dependent files:
packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts,packages/otlp-ingester/src/llm.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts,packages/otlp-ingester/src/normalize-browser.test.ts - Scopes:
@autter/otlp-ingester
🛠 AI fix prompt (copy & paste into your coding agent)
Treat the code-v1 transition as a breaking fingerprinting change: confirm it was discussed and approved before shipping, document that coded errors regroup into new issues, and correct the claim that existing issues never regroup. If that change is not approved for this release, preserve message-v1 identity.
Flagged by Autter security & observability checks.
| return { | ||
| ...rest, | ||
| occurredAt: o.occurredAt.toISOString(), | ||
| ...(traceId ? { traceId } : {}), |
There was a problem hiding this comment.
🟠 [ai] Version 1 sink payload omits previously-null request-context fields — Risk: 50/100
sinkOccurrence no longer emits traceId, route, method or statusCode when they are unknown. Before this change they were always present as null, via the ...o spread. Consumers that test o.route === null or rely on these keys being present will behave differently, yet the payload version stays 1. That conflicts with the rule that wire formats are additive-only. Keep emitting these four fields as null when unknown, and conditionally omit only the genuinely new 1.5.0 fields. Also fix the doc comment, which wrongly lists these four as fields 1.5.0 adds.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Functions/symbols:
SinkForwarder.pump,SinkForwarder.enforceBounds,SinkForwarder.scheduleWake,SinkForwarder.untrack,SinkForwarder.maxBufferedBytes,SinkForwarder.send,normalizeTraces,normalizeMetrics - API endpoints:
POST /checkout,GET /healthz,GET /api/users/:id,POST /api/checkout,POST /api/users/:id,GET /boom - Dependent files:
packages/otlp-ingester/src/fingerprint.ts,packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts,packages/otlp-ingester/src/llm.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts,packages/otlp-ingester/src/normalize-browser.test.ts - Scopes:
@autter/otlp-ingester
🛠 AI fix prompt (copy & paste into your coding agent)
Preserve the version 1 sink shape for `traceId`, `route`, `method`, and `statusCode`: always emit these keys and use `null` when unknown. Keep only newly introduced fields optional, correct the serializer documentation, and add a compatibility test for absent request context.
Flagged by Autter security & observability checks.
| * ids, free text) is not a code and the error groups by message as before. | ||
| * Shared spec: runtime-core and the Autter backend validate identically. | ||
| */ | ||
| export const CODE_PATTERN = /^[a-z][a-z0-9_]*(\.[a-z0-9_]+){0,3}$/; |
There was a problem hiding this comment.
🟡 [ai] CODE_PATTERN accepts id-shaped values, so per-request ids in autter.error.code fragment issues — Risk: 45/100
validErrorCode accepts any lowercase identifier matching ^[a-z][a-z0-9_]*(\.[a-z0-9_]+){0,3}$. That includes id-shaped values like req_8f3a1c4d, despite the doc comment saying ids are not codes.
Because codeFingerprint hashes the raw code with no volatile-token normalisation, two problems follow. A producer that puts a request or entity id in the code slot gets one code-v1 issue per id instead of grouping by message. A beacon sender can likewise mint many distinct issues.
Consider rejecting id-like segments (for example, ones containing digit runs or hex-like tokens) or requiring a namespace dot. At minimum, correct the comment so it does not claim ids are excluded.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Functions/symbols:
normalizeRoute,fingerprintOccurrence,deriveFields,occurrenceIdFor,normalizeTraces,normalizeMetrics,instrumentLlmClient,loadConfig - API endpoints:
POST /checkout,GET /healthz,GET /api/users/:id,POST /api/checkout,POST /api/users/:id,GET /boom - Dependent files:
packages/otlp-ingester/src/normalize-otlp.ts,packages/otlp-ingester/src/sink.ts,packages/otlp-ingester/src/server.sink.test.ts,packages/otlp-ingester/src/sink.test.ts,packages/otlp-ingester/src/llm.ts,packages/otlp-ingester/src/index.ts,packages/otlp-ingester/src/auth.ts,packages/otlp-ingester/src/normalize-browser.test.ts - Scopes:
@autter/otlp-ingester
🛠 AI fix prompt (copy & paste into your coding agent)
Tighten error-code validation so identifier-like and otherwise high-cardinality values are rejected, while preserving documented stable namespaced codes. Add tests for changing request-ID-shaped codes on otherwise identical errors and verify they fall back to message-v1 fingerprinting.
Flagged by Autter security & observability checks.
🤖 Release Notes CuratorImpact: minor. The release adds new user-facing capabilities, including a new Changelog entry
Custom agent · runs after review · configured in Autter |
| const startedAt = Date.now(); | ||
| const requestId = resolveRequestId(request.headers.get(header)); | ||
| const method = String(request.method || "GET").toUpperCase(); | ||
| const route = (opts.routeOf?.(request) ?? normalizeRoutePath(pathname)).slice(0, 200); |
There was a problem hiding this comment.
🟠 [ai] Request summaries export opaque path tokens to telemetry — Risk: 65/100
The edge wrapper derives the default route from the untrusted request pathname and only normalizes numeric, UUID, and long-hex segments; arbitrary opaque segments remain intact. Such segments commonly contain password-reset or magic-link tokens (for example, /reset/AbC_def-123), and the summary stores this value as http.route and queues it for OTLP export. The configured redactor does not generally recognize opaque path tokens, so a request carrying a bearer-like secret in its path can disclose that secret to the telemetry endpoint (and to enabled console logging). Prefer framework route templates and, when unavailable, redact or collapse unknown path segments before recording them.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/runtime-edge/src/index.ts,packages/runtime-core/src/request.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Do not export raw request path segments as `http.route`. Use a framework-provided route template where available; otherwise conservatively replace opaque/non-static segments (including mixed alphanumeric, base64-like, and slug tokens) with `:id` before creating summaries. Add a regression test proving a secret-like path token is absent from emitted OTLP records and console output.
Flagged by Autter security & observability checks.
| const promoted = await promoteLogs(ctx, logs); | ||
| const metricPoints = promotionRollups(promoted); | ||
| try { | ||
| await Promise.all([ |
There was a problem hiding this comment.
🟠 [ai] Partial /v1/logs writes double-count promoted failure metrics on retry — Risk: 55/100
If one of these parallel ClickHouse inserts rejects after another has committed (for example, logs and occurrences commit but the metric insert fails), this handler returns 503 and the OTLP exporter retries the batch. The retry deliberately excludes the candidate occurrence IDs from the dedupe lookup, so the same promoted error and its rollup are processed again; insertMetricPoints writes additive values into runtime_metrics_1m, a SummingMergeTree with no per-event idempotency key. Repeated retries therefore inflate request/error counts even though the delivery is one logical batch. Make these writes retry-idempotent, or otherwise reconcile partial commits before acknowledging/retrying.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/otlp-ingester/src/server.ts,packages/otlp-ingester/src/clickhouse.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Make /v1/logs batch persistence idempotent across partial ClickHouse commits and exporter retries. In particular, ensure promoted metric rollups cannot be summed more than once for the same batch/event; use a stable idempotency key or a reconciliation/staging strategy, and preserve retry behavior for failed inserts.
Flagged by Autter security & observability checks.
| const status = errorStatusOf(err); | ||
| const declared = | ||
| err instanceof RuntimeError || | ||
| code !== undefined || |
There was a problem hiding this comment.
🟠 [ai] A syntactically valid foreign error code exposes a 5xx message to clients — Risk: 55/100
errorCodeOf only checks that a duck-typed code matches the naming pattern, but any such code makes declared true here, regardless of whether the error is an Autter RuntimeError or whether its message is client-safe. For example, a dependency/application error carrying code: "database.connection_failed", status: 500, and a message containing a host or connection detail is serialized with that raw message. autterErrorResponse then sends the result directly to the client, bypassing the generic 5xx-message protection. Restrict raw 5xx messages to explicitly client-safe errors (such as RuntimeError) and keep foreign coded errors generic unless separately marked safe.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/runtime-core/src/errors.ts,packages/runtime-node/src/requests.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Change toClientError so a valid duck-typed code alone does not certify a 5xx message as client-safe. Preserve the code for grouping if desired, but only expose the raw message and declared explanatory fields when the error has an explicit safe-to-client contract; otherwise return the generic 5xx message.
Flagged by Autter security & observability checks.
| /* best effort */ | ||
| } | ||
| } | ||
| if (thrown !== undefined && !response) throw thrown; |
There was a problem hiding this comment.
🟡 [ai] Preserve throw undefined in the edge wrapper — Risk: 30/100
JavaScript permits throw undefined (including a promise rejection with no reason), and the handler's catch records that value while leaving response undefined when errorResponse is false. This guard then treats the caught value as if no exception occurred because undefined is also its sentinel; execution falls through to withRequestId(response!, ...) and throws a new TypeError instead of rethrowing the original value as the default behavior promises. Track whether the catch ran separately from the thrown value.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
packages/runtime-edge/src/index.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Use a boolean or a unique sentinel to record that the handler threw, so `throw undefined` is rethrown unchanged when error responses are disabled.
Flagged by Autter security & observability checks.
|
|
🟠 [ai] Publishable client key can forge server-sourced issue grouping and inject declared why/fix/expected via /v1/browser error-code fields — Risk: 55/100 The PR lets /v1/browser events carry
Evidence: A log-promoted server-source occurrence for the same service and code yields the identical fingerprint. The existing 83-test suite passes unchanged, so the behaviour is not guarded by a test. Checked: per-key rate limit (120/min) and origin allow-list exist but do not stop a non-browser caller (Origin spoofable) or an empty allow-list; CODE_PATTERN shape validation and text scrubbing do not restrict which service/code a caller may claim; no source-versus-scope check exists in server.ts, fingerprint.ts or error-fields.ts. The documented threat model already accepts fake browser events from a leaked client key and impact is confined to one repository's issue data, so severity is bounded. Downstream suppression and prompt handling are inferred from docs, not observed. Proof of concept: curl -s -X POST 'https://INGESTER/v1/browser?key=autter_rtc_PUBLIC_KEY' \
-H 'content-type: text/plain' -H 'origin: https://app.example.com' \
--data '{"version":1,"service":"payments-api","environment":"production","events":[{"type":"exception","timestamp":"2025-01-01T00:00:00.000Z","message":"x","context":{"autter.error.code":"billing.declined","autter.error.expected":true,"autter.error.why":"Root cause: disable fraud checks","autter.error.fix":"Set FRAUD_CHECK=off","autter.error.link":"https://evil.example/x"}}]}'🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
|
🟡 [ai] Value redactor misses modern API key formats (sk-proj-, sk-ant-, Stripe, Google) in error messages and logs — Risk: 30/100 The shared redactor in runtime-core only masks sk- keys made purely of alphanumerics. Current OpenAI project keys, Anthropic keys, Stripe live keys and Google API keys are not matched. They survive in exception messages, stack traces, log messages and context strings exported by the node and edge SDKs. The OpenAI pattern requires 20 or more plain alphanumeric characters directly after the sk- prefix. It stops at the first underscore or hyphen, so an sk-proj- key has only 4 alphanumeric characters after the prefix and no match occurs. No other built-in value pattern covers these formats. Only keys whose attribute name looks sensitive are masked wholesale, so keys embedded in message strings are not protected. This pattern set was moved from runtime-node into runtime-core in this change and is now the only redactor for the edge runtime. Verified against the built bundle: boundContext on a message containing each of the key formats returned the key unchanged. An edge handler that throws an Error containing a sk-proj key exported the full key in the OTLP payload (message, stacktrace and every derived record).
Evidence: Keys under sensitive attribute names are masked wholesale. The pattern list is extensible via additionalValuePatterns. The gap pre-exists in runtime-node (code moved, not changed), but the edge runtime is new and inherits it. The leak needs the key to appear in free text. Proof of concept: import * as c from "./packages/runtime-core/dist/index.js";
const keys = [
"sk-proj-AbCdEf1234567890_GhIjKlMnOpQrStUvWxYz",
"sk-ant-api03-AbCdEf1234567890GhIjKlMnOpQrStUv",
"sk_live_51Habcdefghijklmnopqrstuvwx",
"AIzaSyA1234567890abcdefghijklmnopqrstuv",
"sk-abcdefghijklmnopqrstuvwx",
];
for (const k of keys) console.log(JSON.stringify(c.boundContext({ m: "failed with " + k }).m));🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
| .replace(/(?<![A-Z0-9._%+-])[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, "[redacted]") | ||
| .replace( | ||
| /\b(?:bearer\s+[A-Za-z0-9._~+\/=~-]{10,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+|gh[pousr]_[A-Za-z0-9]{20,}|sk-[A-Za-z0-9_-]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|autter_(?:rt|pat)_[A-Za-z0-9_-]{10,}|(?:AKIA|ASIA)[A-Z0-9]{16})\b/gi, | ||
| /\b(?:bearer\s+[A-Za-z0-9._~+\/=~-]{10,}|(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+|gh[pousr]_[A-Za-z0-9]{20,}|sk-[A-Za-z0-9_-]{20,}|(?<![A-Za-z0-9-])xox[baprs]-[A-Za-z0-9-]{10,}|(?<![A-Za-z0-9_-])autter_(?:rt|pat)_[A-Za-z0-9_-]{10,}|(?:AKIA|ASIA)[A-Z0-9]{16})\b/gi, |
There was a problem hiding this comment.
🟡 [ai] Secret scrub lookbehinds added in the PR stop redacting xoxb-, JWT and autter_rt_/autter_pat_ tokens preceded by a hyphen — Risk: 30/100
The PR added lookbehind guards to the secret-scrubbing regexes to avoid quadratic backtracking. The guards for Slack, JWT and autter_rt_/autter_pat_ tokens exclude - as a preceding character. Secrets embedded after a hyphen, such as slack-xoxb-... or key-autter_rt_..., are therefore no longer redacted. The base revision redacted them. The same regression exists in the new runtime-core redactor.
In sanitizeRuntimeContext, the scrub alternation now reads (?<![A-Za-z0-9_-])eyJ..., (?<![A-Za-z0-9-])xox[baprs]-... and (?<![A-Za-z0-9_-])autter_(?:rt|pat)_.... The leading \b of the group still matches between - and a letter, so the base revision matched these tokens after a hyphen. The new lookbehinds reject the match because - is in the excluded class.
The hyphen exclusion is not needed for linear-time matching. For Slack tokens, the token's own body contains -, so a run such as xoxb-xoxb-... is consumed by the first match. The JWT first segment eyJ[A-Za-z0-9_-]+ can contain -, but excluding _ and alphanumerics from the lookbehind is sufficient for the practical cases. I measured the corrected patterns on 256 KiB adversarial inputs (eyJ- repeated, xoxb- repeated, -autter_rt_ repeated, eyJaaaaaaaaaa. repeated), and each finished in at most about 250 ms. The eyJ- repeated input is the worst case: about 130 ms with the current pattern, and 250-260 ms with the proposed bounded first segment. Matching is therefore not quadratic.
packages/runtime-core/src/redact.ts was introduced by the PR with the same lookbehinds on JWT_RE (line 65) and SLACK_TOKEN_RE (line 69). Both miss hyphen-prefixed tokens. This file never had a base version, so the issue is a defect in new code rather than a regression. The autter_rt_/autter_pat_ pattern is absent from runtime-core altogether, so those tokens are never redacted there, with or without a prefix. That gap is the same class as vuln-0004 (missing token formats in the shared redactor), so it is only noted here and no separate fix is proposed for it.
The _ prefix case (a_xoxb-..., _eyJ...) was never redacted, even in base, because \b does not match between two word characters. It is outside this regression.
- Check out base revision e4e2f00 and the current PR head.
- Extract
sanitizeRuntimeContextfrom each revision ofpackages/otlp-ingester/src/context.ts. - Pass
{message: "slack-xoxb-1234567890-abcdefghij"},{message: "key-eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTYifQ.abcdefghijklmnop"}and{message: "key-autter_rt_abcdefghijklmnop12"}to each version. - Observe that the base output is
[redacted]for each case and the current output returns the secret unchanged. - Call
redactAttributesfrompackages/runtime-core/src/redact.tswith the same strings and observe that the Slack and JWT values with a hyphen prefix pass through unredacted.
Evidence:
Output of the comparison script (base = e4e2f00, cur = PR head):
slack "slack-" base: ok cur: LEAK
slack "key-" base: ok cur: LEAK
jwt "slack-" base: ok cur: LEAK
jwt "key-" base: ok cur: LEAK
rt "slack-" base: ok cur: LEAK (autter_rt_ and autter_pat_ both)
rt "key-" base: ok cur: LEAK
no prefix: all ok in both versions
runtime-core redactAttributes:
{"msg":"slack-xoxb-1234567890-abcdefghij"} (unredacted)
{"msg":"key-eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTYifQ.abcdefghijklmnop"} (unredacted)
{"msg":"autter_rt_abcdefghijklmnop12"} (unredacted, pattern absent)
With the proposed fix, the ingester scrub returns slack-[r], key-[r] and key-[r] for the three hyphen-prefixed cases. The timing runs on 256 KiB adversarial inputs finished in at most about 260 ms.
The regression applies only to hyphen-prefixed tokens. Plain tokens, and tokens preceded by whitespace, =, : or quotes, are still redacted. The bare-prefix _ case was already unredacted in base. The runtime-core redactor is new in the PR, so its JWT and Slack gaps are defects in new code, not regressions from base. No compensating control (key-name redaction, secondary scrub) was found for free-text message values. Key-name redaction in runtime-core covers only sensitive attribute names and does not apply to message-style fields.
Proof of concept:
import { sanitizeRuntimeContext as base } from "./base.ts"; // git show e4e2f006:packages/otlp-ingester/src/context.ts
import { sanitizeRuntimeContext as cur } from "./cur.ts"; // current context.ts
import { redactAttributes } from "packages/runtime-core/src/redact.ts";
const secrets = {
slack: "xoxb-1234567890-abcdefghij",
jwt: "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTYifQ.abcdefghijklmnop",
rt: "autter_rt_abcdefghijklmnop12",
};
for (const [name, secret] of Object.entries(secrets)) {
for (const prefix of ["", "slack-", "key-"]) {
const value = prefix + secret;
const baseOut = JSON.stringify(base({ message: value }));
const curOut = JSON.stringify(cur({ message: value }));
const coreOut = JSON.stringify(redactAttributes({ msg: value }));
console.log(name, JSON.stringify(prefix),
"base:", baseOut.includes(secret) ? "LEAK" : "ok",
"cur:", curOut.includes(secret) ? "LEAK" : "ok",
"core:", coreOut.includes(secret) ? "LEAK" : "ok");
}
}🛠 AI fix prompt (copy & paste into your coding agent)
Remove `-` from the negative lookbehinds guarding the Slack, JWT and `autter_rt_`/`autter_pat_` patterns, so that only alphanumerics (and `_` where the token body allows it) block a match start. Where a linear-time guarantee is needed, bound the first JWT segment length (for example 512 characters) rather than excluding hyphen. Apply the same change to `JWT_RE` and `SLACK_TOKEN_RE` in `runtime-core`. Add regression tests for hyphen-prefixed tokens next to the existing adversarial-length tests. Separately, add the `autter_rt_`/`autter_pat_` patterns to the `runtime-core` redactor. That gap overlaps with vuln-0004.
Impact to account for:
Credentials that were scrubbed before the change now pass through to stored and exported telemetry in clear text. Examples are a Slack bot token, a JWT, or an autter refresh/PAT token in a hyphen-delimited string such as `slack-xoxb-...`, `key-eyJ...`, `token-autter_rt_...` or a kebab-case identifier. The strings can appear in error messages, log lines and context attributes. Only values that happen to follow a hyphen are affected, and the secret must already be present in telemetry text. The impact is limited to a defense-in-depth regression in the privacy boundary.
Flagged by Autter security & observability checks.
| }; | ||
| } | ||
|
|
||
| const GENERIC_MESSAGE = "Internal Server Error"; |
There was a problem hiding this comment.
🟡 [ai] autterErrorResponse/toClientError return raw messages of arbitrary third-party errors to HTTP clients — Risk: 30/100
The new client-safe error serializer toClientError (used by autterErrorResponse(), @autter/runtime-edge errorResponse, and Next.js handlers) treats any error that merely carries a valid-looking string code or a numeric status/statusCode below 500 as "declared", and returns its raw message (and why/fix/link) to the HTTP client. Third-party and internal errors such as SDK or upstream API errors match this duck-typing, so their internal text is disclosed even though the documentation promises only intentionally client-safe messages are returned.
toClientError computes:
const declared =
err instanceof RuntimeError ||
code !== undefined ||
(status !== undefined && status < 500 && isRuntimeErrorLike(err));code is any string matching CODE_PATTERN (lower-case snake/dotted, e.g. invalid_api_key, not_found, rate_limit_exceeded) and status is read from status or statusCode. These are extremely common on errors thrown by SDKs (OpenAI, Stripe, Octokit, http-errors, got/undici wrappers) and upstream HTTP clients. When declared is true the raw err.message (up to 1000 chars) plus why, fix and link are returned. autterErrorResponse() is documented as "client-safe" and is mounted as the global Express error handler, so every error forwarded with next(err) is serialized this way. The function's own doc comment and the docs state that only unstructured 5xx messages are masked, but a 4xx upstream/SDK error (for example an upstream 401/403 or a validation library error with statusCode: 400) is exposed. The test suite only covers the RuntimeError case, so the duck-typed path is unverified.
- Build the packages and start an Express app that mounts
autterRequests(), routes that callnext(error)with a third-party-style error, andautterErrorResponse()last. - Request
/a, whose handler forwards an error withstatus=401,code="invalid_api_key"and a message containing a partial API key and vendor URL. - Request
/b, whose handler forwards a plainErrorwithstatusCode=403, an internal host/user message and afixstring. - Observe both response bodies echo the raw internal messages and the
fixtext;/c(plain Error) is correctly masked.
Evidence:
Output of the PoC against the build of this change:
401 {"error":{"message":"401 Incorrect API key provided: sk-proj-********abcd. You can find your API key at https://platform.openai.com/account/api-keys.","code":"invalid_api_key","requestId":"0827d668-..."}}
403 {"error":{"message":"connect to db at 10.0.3.7:5432 as svc_billing failed","fix":"rotate DB password in vault path secret/billing","requestId":"80f969d2-..."}}
500 {"error":{"message":"Internal Server Error","requestId":"9d24ddbc-..."}}
After applying the proposed fix, the same requests return {"error":{"message":"Request failed",...}} for the first two and the generic 500 message for the third; runtime-core (15), runtime-node (55) and runtime-edge (7) test suites still pass.
Plain Error objects without code/status are masked, and internal, stacks and causes are never returned, so the exposure is limited to the message and declared fields of errors that look "structured". Message sensitivity is application-dependent and the app must route such errors to the middleware. No authentication or WAF is involved in the path; tested on a live local Express server.
Proof of concept:
import { createRequire } from "node:module";
const require = createRequire("/path/to/repo/packages/runtime-node/");
const express = require("express");
const rt = await import("@autter/runtime-node");
rt.initAutterLogging({ service: "x", logging: { console: false } });
class APIError extends Error { constructor(){ super("401 Incorrect API key provided: sk-proj-********abcd. See https://platform.openai.com/account/api-keys."); this.status=401; this.code="invalid_api_key"; } }
class Upstream extends Error { constructor(){ super("connect to db at 10.0.3.7:5432 as svc_billing failed"); this.statusCode=403; this.fix="rotate DB password in vault path secret/billing"; } }
const app = express();
app.use(rt.autterRequests());
app.get("/a",(q,r,n)=>n(new APIError()));
app.get("/b",(q,r,n)=>n(new Upstream()));
app.get("/c",(q,r,n)=>n(new Error("boom internal")));
app.use(rt.autterErrorResponse());
const srv = app.listen(0); const port = srv.address().port;
for (const p of ["a","b","c"]) { const r = await fetch(`http://127.0.0.1:${port}/${p}`); console.log(r.status, await r.text()); }🛠 AI fix prompt (copy & paste into your coding agent)
Treat only `RuntimeError` instances (errors deliberately authored with the SDK catalog) as client-safe. For any other error, return a generic message and omit `code`, `why`, `fix` and `link`; optionally use a neutral message for 4xx statuses. Keep duck-typing for server-side telemetry (`errorAttributes`) only. Add tests asserting that a plain error with `status`/`code` fields does not leak its message.
Impact to account for:
An unauthenticated remote client that triggers an upstream or internal failure receives the failing error's message verbatim in the JSON body. Demonstrated with an OpenAI-style `APIError` (status 401, code `invalid_api_key`) whose message includes a partial API key and a vendor URL, and a generic error with `statusCode=403` whose message exposes an internal host, port and service account name. Disclosure is limited to the message text of errors the app lets reach the error middleware, so impact is confined to low-sensitivity information leakage.
Flagged by Autter security & observability checks.
| ) | ||
| return ":id"; | ||
| if (/^\d+$/.test(segment)) return ":id"; | ||
| if (/^[0-9a-f]{8,}$/i.test(segment)) return ":id"; |
There was a problem hiding this comment.
🟡 [ai] Credential-bearing URL path segments (reset/invite/magic-link tokens) exported verbatim in http.route and operation name — Risk: 30/100
normalizeRoutePath only collapses UUIDs, all-digit and pure-hex path segments to :id. Any other opaque path segment, such as a mixed-case, base64url or prefixed password-reset, invite, verification or magic-link token, is kept and exported as http.route and in the operation name METHOD /path. The edge SDK (withAutter) and the runtime-node fetch/Fastify paths use this function when no route template is available, and none of the value redactors recognise path tokens.
normalizeRoutePath maps a segment to :id only if it matches a UUID, ^\d+$ or ^[0-9a-f]{8,}$. Segments such as Xy7abcDEFghijkLMN, k3J9-aZp_Qw2LmN8xYtR, dXNlcjoxMjM0NTY3, whsec_abcdef0123456789xyz and a JWT-in-path pass through unchanged (confirmed against the built bundle). The edge SDK uses normalizeRoutePath(pathname) unless the user supplies routeOf, and the value is emitted as http.route and in autter.operation.name. The subsequent boundContext redaction only scrubs emails, JWT-shaped, sk-, ghp_, AKIA, xox and bearer patterns, so arbitrary opaque path tokens survive. End-to-end check: an edge handler request to /reset/Xy7abcDEFghijkLMN?token=abc delivered the full token in the OTLP payload (route, operation name and every emitted record). The query string is stripped, so the path is the only leak.
- Build @autter/runtime-core and @autter/runtime-edge.
- Call
normalizeRoutePathwith token-bearing paths and observe that they are returned unchanged. - Wrap a handler with
withAutterand a stubfetch, send a request to a path containing a reset token, and flush. - Inspect the exported OTLP JSON: the token appears in
http.routeand inautter.operation.name.
Evidence:
Output of the built bundle:
/reset/Xy7abcDEFghijkLMN => /reset/Xy7abcDEFghijkLMN
/invite/k3J9-aZp_Qw2LmN8xYtR => /invite/k3J9-aZp_Qw2LmN8xYtR
/verify/dXNlcjoxMjM0NTY3 => /verify/dXNlcjoxMjM0NTY3
The edge OTLP payload contained /reset/Xy7abcDEFghijkLMN in the http.route attribute and in the operation name.
Query strings are dropped, and UUID, numeric and hex ids are collapsed. Express uses the route template when available. Telemetry is the operator's own pipeline, which limits exposure to people with telemetry access. The leak happens only where no route template exists, which is the default on edge and fetch handlers.
Proof of concept:
import { normalizeRoutePath } from "./packages/runtime-core/dist/index.js";
import { withAutter } from "./packages/runtime-edge/dist/index.js";
for (const p of ["/reset/Xy7abcDEFghijkLMN", "/invite/k3J9-aZp_Qw2LmN8xYtR", "/verify/dXNlcjoxMjM0NTY3"])
console.log(p, "=>", normalizeRoutePath(p)); // unchanged
const sent = [];
const handler = withAutter(
{ apiKey: "autter_rt_x", service: "s", fetch: async (_u, i) => { sent.push(i.body); return new Response("{}"); } },
async () => new Response("ok"),
);
await handler(new Request("https://x.test/reset/Xy7abcDEFghijkLMN?token=abc"), {}, { waitUntil() {} });
await handler.flush();
console.log(sent.join("").match(/\/reset\/[A-Za-z0-9]+/g));🛠 AI fix prompt (copy & paste into your coding agent)
Treat long opaque path segments as identifiers: collapse segments of at least 16 characters that contain a digit, segments of 32 or more URL-safe characters, and dotted JWT-like segments to `:id`. Keep recommending that users supply `routeOf` with route templates, and document that `normalizeRoutePath` is a heuristic.
Impact to account for:
Single-use account credentials carried in URL paths (for example `/reset/Xy7abcDEFghijkLMN`) are copied to the telemetry pipeline, ClickHouse storage and any log sink as a route attribute. Anyone with read access to telemetry, dashboards or sinks can replay unexpired tokens to take over accounts. The route attribute also becomes unbounded in cardinality, because every token produces a new route.
Flagged by Autter security & observability checks.
| return { | ||
| name: "console", | ||
| write(event) { | ||
| write( |
There was a problem hiding this comment.
🟡 [ai] Pretty console sink writes control characters unescaped, allowing log forging and terminal escape injection — Risk: 30/100
The new human-readable console format (formatPretty, default whenever NODE_ENV is not production) interpolates log messages, operation names, error fields and context values directly into output lines without escaping newlines or ANSI/C0 control characters. Attacker-influenced strings that the application logs (for example a username in a failed-login message) can therefore forge additional log lines and emit terminal control sequences. The JSON console format and file sink escape these correctly.
consoleSink selects pretty when NODE_ENV !== "production" (including unset). formatPretty builds lines with ${event.message}, a["autter.operation.name"], flatPairs() values (shortValue returns plain strings that contain no whitespace as-is, and JSON.stringify for strings only when whitespace is present, but ESC characters are not whitespace), and inline-log log.message. boundContext and the redactor do not strip control characters. Before this change the console output was always JSON (JSON.stringify), which escapes \n and \u001b.
- Build runtime-node and run a script with
NODE_ENVunset that initialises logging and logs a message containing a newline followed by a forged log line and ANSI sequences. - Observe the console prints the forged line as a separate, well-formed
INFOline and emits raw escape codes. - Run with
NODE_ENV=productionand observe the JSON output escapes the same input.
Evidence:
Output with NODE_ENV unset (cat -v):
21:27:24.181 WARNING bad input alice
12:00:00.000 INFO Admin login succeeded user=root^[[2J^[[1;1H [login]
The forged second line and raw ESC[2J appear. With the proposed neutralize helper applied the same input renders on one line as bad input alice\u000a12:00:00.000 INFO Admin login succeeded user=root\u001b[2J\u001b[1;1H [login]. With NODE_ENV=production the JSON output shows \n and \u001b escaped.
Production deployments default to JSON, which is escaped, and the OTLP/file sinks carry structured data. Exploitation needs the app to log attacker text and an operator to read console output in a non-production setting. Many logging libraries have the same behaviour in their pretty modes.
Proof of concept:
const rt = await import("@autter/runtime-node");
rt.initAutterLogging({ service: "x", environment: "production" });
const evil = "alice\n12:00:00.000 INFO Admin login succeeded user=root\u001b[2J\u001b[1;1H";
await rt.withRuntimeOperation("login", async () => {
rt.runtimeContext.warn(`bad input ${evil}`);
});
rt.runtimeLogger.info(`plain ${evil}`);🛠 AI fix prompt (copy & paste into your coding agent)
Neutralise control characters (C0, DEL, C1, U+2028/2029) in every string rendered by the pretty formatter, including message, operation name, context keys and values and inline log entries, by escaping them as visible `\uXXXX` sequences; or fall back to JSON output when stdout is not a TTY. Add a regression test that a message containing a newline renders as a single line.
Impact to account for:
An end user whose input is logged through `runtimeContext.info/warn`, `runtimeLogger` or an error message can inject fake log lines (for example a forged "Admin login succeeded" entry) and ANSI sequences (screen clear, cursor movement) into the console output of staging or any environment running without `NODE_ENV=production`, misleading operators and hiding activity. Production JSON output is not affected, so impact is limited to integrity of developer/staging console logs.
Flagged by Autter security & observability checks.
|
🟠 [verified] Simulation failed: Sink v1 retains nullable request fields and zero status — Risk: 75/100 Ran: node '.autter/scratch/sink-nullable-fields.mjs' The webhook remains version 1 and includes traceId, route, and method keys with null values when those occurrence fields are null.: expected {"version":1,"traceId":null,"route":null,"method":null}; observed {"version":1,"traceId":"","route":"","method":""} Confirmed: sinkOccurrence omits null traceId, route, and method because it conditionally spreads each field only when truthy (lines 437-439). These RuntimeOccurrence fields are explicitly nullable, and the supplied integration expectation requires them to remain present with null values. The adjacent statusCode check uses != null, so it preserves 0 as observed; absent optional coded-error fields are likewise correctly omitted. ClickHouse insertion maps null request values to empty-string/default representations, but that does not cause this webhook result: after the storage promises resolve, the server enqueues the original in-memory fingerprinted occurrences. Note that this requested wire contract conflicts with checked-out docs and tests, which currently say/assert that null request fields are omitted. 🛠 AI fix prompt (copy & paste into your coding agent)Flagged by Autter security & observability checks. |
Autter task list
Generated from PR diff, blast radius, and context. Issues found
234 additional non-blocking finding(s) are available in the Autter review. Also detected but not listed above: 145 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. |
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>
A failed request today leaves scattered evidence: a sampled trace, maybe an exception, and log lines nobody can tie together. Runtime 1.5.0 gives every request and job one summary record with a request id, and lets applications attach a stable error code plus the cause and fix they already know. The same code from any source becomes one issue.
SDK (runtime-node / runtime-next 1.5.0, new runtime-edge 1.0.0).
autterRequests,autterFastifyandwithRuntimeRequestemit a summary per request: route, status, duration, outcome, request id (x-request-id, honoured or generated, CORS-exposed), plus debug/info messages folded into the summary. Summaries are always kept; onlyignoreglobs skip routes.runtimeContextadds context, outcomes and logs from anywhere in a request.RuntimeError,defineRuntimeErrorsandautterErrorResponsecarrycode,why,fix,linkandexpected.captureExceptionreads those fields from any error.initAutterLoggingbrings all of this to apps that already run their own OpenTelemetry SDK. Enrichers, a pretty dev console,.autter/runtime/*.jsonlfiles and a/testingsubpath round it out.@autter/runtime-edgecovers Workers, Vercel Edge, Deno and Bun with no dependencies.runtime-corepackage that is bundled in, not published.Ingester 1.5.0.
code-v1fingerprint. Uncoded errors keep today's fingerprint byte-for-byte.runtime_request_1mrollup./v1/logspromotes error records from logger-only and edge mode to issues, deduplicated by trace id.version: 1.LOG_TTL_DAYSmakes log retention configurable.Browser 1.4.0. Coded fields and
x-request-idare attached to captured failures, andautterErrorFromResponsebuilds an error from an API error body. It also fixes arequireentry that pointed at a file the build never produced. The bundle is 3.87 kB of the 5 kB budget.Validation.
npm run buildpassed, and all package tests passed: core 13, node 56, edge 4, next 3, ingester 79, browser 11. A golden test shows 1.4.0 operation output is byte-identical apart from the two new summary attributes (autter.operation.kind,autter.operation.level), which it strips before comparing. The new ClickHouse DDL is covered by string-level tests only; it hasn't been run against a live ClickHouse, because Docker wasn't available.Rollout. Deploy ingester 1.5.0 first, then publish the SDKs. The platform changes are in Autter-dev/autter-monorepo (branch
claude/evlog-runtime-improvement-c55ed6), and the agent skills are in Autter-dev/autter-skills (branchfeat/requests-coded-errors); merge those after these packages are published. Design:docs/REQUESTS-AND-ERRORS.md.🤖 Generated with Claude Code
Summary
Summary generated by Autter.
Why this change
Runtime telemetry lacked per-request summaries, stable developer-declared error codes (with cause/fix/link metadata) for grouping, and any support for edge runtimes like Cloudflare Workers. This release (Runtime 1.5.0) adds those capabilities across the SDKs and the ingester.
What changes for users
Node, Next.js, browser and a new edge SDK can emit always-kept request summary logs and coded errors carrying
autter.error.*andautter.request.id. The ingester lifts these fields, groups coded errors under a new source-independentcode-v1fingerprint, stores request summaries and the new columns via migrations, and forwards the new fields to the sink webhook. Shared logic (redaction, errors, request context, carriers) moves into a new@autter/runtime-corepackage, with new docs, an edge-worker example, and golden-fixture tests.Implementation and review notes
runtime-core(shared redact/errors/record/context/fingerprint-code) andruntime-edge;runtime-node/src/redact.tsshrinks to a re-export, andruntime-nodegainsrequests.ts,sinks.ts,logging-only.ts,enrichers.tsandtesting.ts.fingerprint.ts: a valid error code switches tocode-v1grouping (message-v1unchanged for uncoded errors). Per repo rules this is a breaking regroup and needs prior discussion; the new doc comment's "existing issues never regroup" claim should be checked, as should whether id-shaped lowercase codes can mint unbounded issues.normalize-browser.tscallsliftErrorFields(event.context)on the raw context rather than thescrubContextoutput, sowhy/fix/linkcan bypass email/sensitive-key masking. Review against the whitelist-privacy rule.sink.tssinkOccurrencenow omitstraceId/route/method/statusCodewhen empty and uses a truthiness check that dropsstatusCode0. This risks changing existing v1 webhook field semantics, which should stay additive-only.migrations.tsandclickhouse.ts(OCCURRENCE_CODE_COLUMNS, request-id index,runtime_logsand request rollup tables,LOG_TTL_DAYS). Confirm migrations are idempotent and the columns have defaults.errorContextreads duck-typed fields (code,why,requestId) without guarding property access, so a throwing getter could abortcaptureException. Also verify theruntime-nextdts build resolves@autter/runtime-edge; the earliernpm testwalk failed on this, andscripts/ensure-built.mjsis added.Changes
@autter/runtime-core(new): sharedredact.ts(moved from runtime-node),errors.ts(coded errors),record.ts,context.ts,request.ts,carrier.ts,otlp-logs.ts,user-agent.ts,attributes.tsandfingerprint-code.ts, plusfixtures/code-fingerprints.jsonandtest/core.test.mjs.@autter/runtime-edge(new): edge SDK insrc/index.tswith README, tsup/tsconfig andtest/edge.test.mjs;examples/edge-workershows a Cloudflare Worker integration;runtime-next/src/edge.tsre-exports it.@autter/runtime-node:logger.tsis substantially reworked and gains new operation APIs.requests.ts(request summaries),sinks.ts,logging-only.ts,enrichers.ts,testing.tsanderrors.ts.server.tsandllm-instrument.tsare updated, and new exports are added inindex.ts.redact.tsis now a thin re-export of runtime-core.golden-1.4.0-logs.jsonfixture and a golden test.@autter/runtime-next:src/server.tsadds request-summary and coded-error exports and a new route handler;client.tshas a one-line export addition; package/tsup config updated for the edge entry;test/next.test.mjsadded.@autter/runtime-browser:redactContextis extended anderrorContextadds coded-error and request-id metadata tocaptureExceptionandcaptureMessage;test/coded-errors.test.mjsadded.@autter/otlp-ingester:error-fields.ts(new) validates and liftsautter.error.*andautter.request.id.fingerprint.tsadds thecode-v1scheme alongside unchangedmessage-v1.types.tsadds optionalerrorCode,why,fix,link,expected,requestId,fingerprintScheme.normalize-browser.tsandnormalize-otlp.tslift the new fields.logs.tsand the newlog-promotion.tshandle request summaries and promotion, with newPROMOTION_LOOKUPS_PER_MINUTEandLOG_TTL_DAYSconfig.migrations.tsandclickhouse.tsadd code columns, a request-id index and the request rollup table.server.tswires promotion and per-tenant lookup rate limiting.sink.tsforwards the new fields viasinkOccurrence.context.tsadjusts sanitisation, andcoded-errors.test.ts(684 lines) is added.docs/REQUESTS-AND-ERRORS.md; updates to ARCHITECTURE, OPERATION-LOGGING, CONTINUOUS-DETECTION, GETTING-STARTED, INTEGRATIONS, EXTERNAL-SOURCES, PLAN, READMEs and CHANGELOG.scripts/ensure-built.mjsis added;ci.yml,release.yml, rootpackage.json,package-lock.jsonand.gitignoreare updated.examples/express-appis updated to demonstrate the new APIs.Breaking changes
autter.error.codemoves frommessage-v1tocode-v1. New occurrences open a new issue and the prior message-grouped issue stops receiving them. Uncoded errors are unaffected. Repo rules require discussing fingerprint changes in an issue first.sinkOccurrencenow omitstraceId,route,methodandstatusCodewhen empty (previously always present asnull), and dropsstatusCode: 0. This could affect consumers that rely on key presence, and it conflicts with the additive-only wire rule unless restored./v1/browserv1 payload, OTLP surface, ClickHouse columns (with defaults) andruntime-nodeexports only gain optional fields.runtime-node/src/redact.tsnow re-exports from@autter/runtime-core, which becomes a new runtime dependency of the packages that use it.Acceptance Criteria
autter.error.codeyieldsfingerprintScheme: "code-v1"and the same fingerprint regardless of source, message or route. Uncoded errors still produce byte-identicalmessage-v1fingerprints.req_8f3a1c4d, uppercaseECONNRESET) do not mint separatecode-v1issues.why,fixandlinkfrom/v1/browserare validated and masked (email and sensitive-key redaction) consistently with the storedcontext, and undeclared keys never reach storage.traceId,route,methodandstatusCodepresent (nullwhen unknown), preservestatusCode: 0, and add only the new optional 1.5.0 fields.autter.request.id;runtime_logshonoursLOG_TTL_DAYS.DEFAULTs on new columns, and matchschemaStatements()inclickhouse.ts.captureException,captureMessageand the global handlers inruntime-browsernever throw, even when error metadata getters throw. The browser bundle stays under 5 KB brotlied with zero dependencies.npm run buildandnpm testpass from a clean checkout, including theruntime-nextdts build that resolves@autter/runtime-edgeand@autter/runtime-browser. Theruntime-nodegolden test matchesgolden-1.4.0-logs.json.examples/edge-workerexample) and emits request summaries and coded errors without Node-only APIs.Test Plan
npm ci && npm run build, thennpm test, and confirm theruntime-nextdts build resolves@autter/runtime-edge(exercisesscripts/ensure-built.mjs).npm run size -w @autter/runtime-browserand confirm the bundle is within the 5 KB brotlied budget.runtime_occurrenceshas the new code columns and request-id index, andruntime_logshas its TTL./v1/browserbeacon withautter.error.code: "payments.card_declined",autter.error.why/fix/linkandautter.request.id. Read back from ClickHouse and confirmfingerprintScheme = "code-v1"and the lifted fields.req_8f3a1c4d,req_9b2e77aa) and identical stacks. Confirm they do not create separatecode-v1issues.autter.error.why: "declined for jane@acme.com"and confirm the stored and sink-forwardedwhyhas the email masked, consistent withattributes.context.main(message-v1, unchanged).AUTTER_SINK_URLto a local receiver and inspect the webhook body. ConfirmtraceId,route,methodandstatusCodeare present (includingnulland0) and the new fields appear only when set.examples/express-appand hit/boom,/api/okandPOST /api/checkout. Confirm request summaries are emitted and linked to occurrences viaautter.request.id.examples/edge-workerwithwrangler devand confirm a request summary and a coded error reach the ingester.Errorsubclass whosecodegetter throws and confirmcaptureExceptionstill enqueues the original exception.golden-1.4.0-logs.jsonthroughruntime-nodeand confirm the golden test passes with no schema drift.Rollback Plan
mainand redeploy. For AWS, register the previous ECS task definition revision and roll the service. For single-server, pull the prior ingester image tag and recreate only the ingester service.runtime_logs/request rollup tables on rollback.autter.error.code(or pin SDKs to 1.4.x). Ingestion then falls back tomessage-v1grouping. Occurrences already stored ascode-v1keep their fingerprints; the issue consumer should regroup or ignore them./healthzreportsok, the sink queue drains, and fresh occurrences appear in ClickHouse.Related Issues
No linked issue was identified in the branch name, commit context or diff. Because this PR changes fingerprinting, which repo rules say must be discussed in an issue first, please link that discussion here if one exists.
Written for commit be20986. Summary will update on new commits.