ca9a74e84a
A static observability scorer for the webapp's route entry points,
Lighthouse-style. The idea comes from evlog's `map` command, but that
tool has no Remix adapter and checks for its own logging API, so the
idea is ported rather than the tool.
It scans all 427 loader/action entry points in `apps/webapp/app/routes`
with the TypeScript compiler API and scores each against five checks:
error-classification, auth-boundary, auth-scope, request-context and
audit-trail. Current output on the real tree is **19/100** over 412
measured entry points.
```
cd internal-packages/observability-map
pnpm exec tsx src/cli.ts # terminal report
pnpm exec tsx src/cli.ts --json # machine output
pnpm exec tsx src/cli.ts api/v1/token # one entry, per-check detail
```
The two findings at the top of the fix list are real: `/auth/sso` and
`/api/v1/authorization-code` mint or exchange credentials
unauthenticated, and `/_app/orgs/:organizationSlug/settings/team`
resolves its org from a URL slug and gates each mutating branch on an
RBAC check alone, which per `apps/webapp/CLAUDE.md` is not the tenant
floor on self-hosted.
Decisions worth knowing, all with the reasoning in the README:
- The score started at 83 during development and fell to 19. Every drop
was a perverse incentive being removed, not a regression: routes were
being paid for having no error handling, two checks were reading the
same fact, suppressing a failure raised the score, and a no-op `catch
(e) { throw e }` was worth 50 points a route.
- **A mutation corpus is the tool's main defence.** 44 entries apply
semantics-preserving edits to a copy of the real route tree and assert
the score cannot rise, per route as well as globally, because a mean can
hide one route going up by taking another down. One entry runs as a live
expected failure: `try { String(0); }` with a deciding catch is a known
open hole worth 19 to 44, and it is disclosed rather than quietly
excluded.
- `audit-trail` and `request-context` are reported as headline figures
rather than one finding repeated hundreds of times. Both still count in
full where they should.
- A cohort change moves the number without anything in the codebase
getting better. Widening the sensitive cohort from 26 to 67 took the
global from 15 to 19 with no webapp change at all, so the report prints
per-check applicability and what the global would be without each one.
CI: a report-only job posts a sticky comment when a PR moves the report,
and says nothing when it does not. The package's own tests gate through
`pr_checks.yml`. The diff-scoped merge gate is still deferred until the
report has been used in anger.
524 tests plus the corpus. No runtime or dependency changes to anything
that ships.
<!-- GitButler Footer Boundary Top -->
---
This is **part 1 of 4 in a stack** made with GitButler:
- <kbd> 4 </kbd> #4485
- <kbd> 3 </kbd> #4484
- <kbd> 2 </kbd> #4483
- <kbd> 1 </kbd> #4455 👈
<!-- GitButler Footer Boundary Bottom -->
166 lines
6.8 KiB
JSON
166 lines
6.8 KiB
JSON
{
|
|
"name": "triggerdotdev",
|
|
"private": true,
|
|
"workspaces": [
|
|
"apps/*",
|
|
"packages/*",
|
|
"integrations/*"
|
|
],
|
|
"version": "0.0.1",
|
|
"scripts": {
|
|
"build": "turbo run build",
|
|
"build:force": "turbo run build --force",
|
|
"build:db:seed": "turbo run build:db:seed",
|
|
"db:migrate": "turbo run db:migrate:deploy --filter=!@internal/run-ops-database && turbo run generate",
|
|
"db:seed": "turbo run db:seed",
|
|
"db:studio": "turbo run db:studio --filter=!@internal/run-ops-database",
|
|
"db:populate": "turbo run db:populate",
|
|
"dev": "turbo run dev",
|
|
"i:dev": "infisical run -- turbo run dev",
|
|
"generate": "turbo run generate",
|
|
"format": "oxfmt .",
|
|
"format:prisma": "pnpm --filter @trigger.dev/database run format:prisma && pnpm --filter @internal/run-ops-database run format:prisma",
|
|
"lint": "oxlint",
|
|
"lint:fix": "oxlint --fix",
|
|
"knip:deps": "knip --production --dependencies",
|
|
"docker": "node scripts/docker.mjs -f docker/docker-compose.yml up -d --build --remove-orphans",
|
|
"docker:stop": "node scripts/docker.mjs -f docker/docker-compose.yml stop",
|
|
"docker:full": "node scripts/docker.mjs -f docker/docker-compose.yml -f docker/docker-compose.extras.yml up -d --build --remove-orphans",
|
|
"docker:full:stop": "node scripts/docker.mjs -f docker/docker-compose.yml -f docker/docker-compose.extras.yml stop",
|
|
"dev:docker": "docker compose -p triggerdotdev-dev-docker -f docker/dev-compose.yml up -d --build --remove-orphans",
|
|
"dev:docker:build": "docker compose -p triggerdotdev-dev-docker -f docker/dev-compose.yml up -d --build",
|
|
"dev:docker:stop": "docker compose -p triggerdotdev-dev-docker -f docker/dev-compose.yml stop",
|
|
"test": "turbo run test --concurrency=1 -- --run",
|
|
"test:webapp": "turbo run test --filter webapp -- --run",
|
|
"test:packages": "turbo run test --concurrency=1 --filter \"@trigger.dev/*\" -- --run",
|
|
"test:internal": "turbo run test --concurrency=1 --filter \"@internal/*\" -- --run",
|
|
"test:dev": "turbo run test:dev",
|
|
"start": "turbo run start",
|
|
"check-exports": "turbo run check-exports",
|
|
"clean": "turbo run clean",
|
|
"clean:node_modules": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +",
|
|
"typecheck": "turbo run typecheck",
|
|
"map": "pnpm --filter @internal/observability-map run map",
|
|
"test:e2e": "playwright test",
|
|
"test:e2e:ui": "playwright test --ui",
|
|
"test:e2e:dev": "turbo run test:e2e:dev",
|
|
"test:e2e:ci": "turbo run test:e2e:ci",
|
|
"setup": "turbo run generate db:migrate:force db:seed",
|
|
"env:pull": "turbo run env:pull",
|
|
"changeset:add": "changeset",
|
|
"changeset:version": "changeset version && pnpm install --lockfile-only && node scripts/bump-helm-chart.mjs && node scripts/cleanup-server-changes.mjs",
|
|
"changeset:release": "pnpm run build --filter \"@trigger.dev/*\" --filter \"trigger.dev\" && changeset publish",
|
|
"changeset:v4": "changeset pre enter v4",
|
|
"changeset:normal": "changeset pre exit",
|
|
"clean:sourcemaps": "turbo run clean:sourcemaps",
|
|
"storybook": "turbo run storybook"
|
|
},
|
|
"devDependencies": {
|
|
"@manypkg/cli": "^0.19.2",
|
|
"@playwright/test": "^1.36.2",
|
|
"@trigger.dev/database": "workspace:*",
|
|
"@types/node": "24.13.3",
|
|
"@vitest/coverage-v8": "4.1.7",
|
|
"autoprefixer": "^10.4.12",
|
|
"knip": "6.25.0",
|
|
"oxfmt": "^0.54.0",
|
|
"oxlint": "^1.69.0",
|
|
"pkg-pr-new": "0.0.75",
|
|
"pkg-types": "1.1.3",
|
|
"tsx": "^3.7.1",
|
|
"turbo": "^1.10.3",
|
|
"typescript": "catalog:",
|
|
"vite-tsconfig-paths": "^4.0.5",
|
|
"vitest": "4.1.7"
|
|
},
|
|
"packageManager": "pnpm@10.33.2",
|
|
"dependencies": {
|
|
"@changesets/cli": "2.26.2",
|
|
"@remix-run/changelog-github": "^0.0.5",
|
|
"agentcrumbs": "^0.5.0",
|
|
"node-fetch": "2.6.x"
|
|
},
|
|
"pnpm": {
|
|
"patchedDependencies": {
|
|
"@changesets/assemble-release-plan@5.2.4": "patches/@changesets__assemble-release-plan@5.2.4.patch",
|
|
"engine.io-parser@5.2.2": "patches/engine.io-parser@5.2.2.patch",
|
|
"redlock@5.0.0-beta.2": "patches/redlock@5.0.0-beta.2.patch",
|
|
"@kubernetes/client-node@1.0.0": "patches/@kubernetes__client-node@1.0.0.patch",
|
|
"@sentry/remix@9.46.0": "patches/@sentry__remix@9.46.0.patch",
|
|
"@upstash/ratelimit@1.1.3": "patches/@upstash__ratelimit.patch",
|
|
"antlr4ts@0.5.0-alpha.4": "patches/antlr4ts@0.5.0-alpha.4.patch",
|
|
"@window-splitter/state@1.1.3": "patches/@window-splitter__state@1.1.3.patch",
|
|
"streamdown@2.5.0": "patches/streamdown@2.5.0.patch",
|
|
"tsup@8.4.0": "patches/tsup@8.4.0.patch",
|
|
"@remix-run/router@1.23.3": "patches/@remix-run__router@1.23.3.patch",
|
|
"@clickhouse/client-common@1.12.1": "patches/@clickhouse__client-common@1.12.1.patch"
|
|
},
|
|
"overrides": {
|
|
"typescript": "catalog:",
|
|
"@types/node": "24.13.3",
|
|
"react@^18": "18.3.1",
|
|
"react-dom@^18": "18.3.1",
|
|
"ai@^6": "6.0.116",
|
|
"@ai-sdk/provider-utils@^4": "4.0.29",
|
|
"express@^4>body-parser": "1.20.3",
|
|
"@remix-run/dev@2.17.5>tar-fs": "2.1.4",
|
|
"tar": "7.5.19",
|
|
"form-data@^2": "2.5.4",
|
|
"form-data@^3": "3.0.5",
|
|
"form-data@^4": "4.0.6",
|
|
"ws@>=7 <7.5.11": "7.5.11",
|
|
"ws@>=8 <8.21.0": "8.21.0",
|
|
"hono@>=4 <4.12.25": "4.12.25",
|
|
"undici@>=6 <6.27.0": "6.27.0",
|
|
"undici@>=7 <7.28.0": "7.28.0",
|
|
"js-yaml@>=3.0.0 <3.14.2": "3.14.2",
|
|
"js-yaml@>=4.0.0 <4.1.1": "4.1.1",
|
|
"jws@<3.2.3": "3.2.3",
|
|
"qs@>=6.0.0 <6.15.2": "^6.15.2",
|
|
"lodash@>=4.17 <4.18.0": "^4.18.0",
|
|
"lodash-es@>=4.17 <4.18.0": "^4.18.0",
|
|
"dompurify@>=3 <3.4.0": "^3.4.1",
|
|
"vite@>=5.0.0 <6.4.2": "^6.4.2",
|
|
"rollup@>=4 <4.59.0": "^4.59.0",
|
|
"flatted@>=3 <3.4.2": "^3.4.2",
|
|
"picomatch@>=2 <2.3.2": "^2.3.2",
|
|
"picomatch@>=4 <4.0.4": "^4.0.4",
|
|
"minimatch@>=3 <3.1.3": "^3.1.3",
|
|
"protobufjs@>=7 <7.5.6": "^7.5.6",
|
|
"fast-xml-parser@>=4 <4.5.5": "^4.5.5",
|
|
"fast-xml-parser@>=5 <5.7.0": "^5.7.0",
|
|
"path-to-regexp@>=0.1 <0.1.13": "^0.1.13",
|
|
"ajv@>=8 <8.18.0": "^8.18.0",
|
|
"socket.io-parser@>=4 <4.2.6": "^4.2.6",
|
|
"postcss@>=8 <8.5.10": "^8.5.10",
|
|
"yaml@>=2 <2.8.3": "^2.8.3",
|
|
"semver@>=5 <5.7.2": "^5.7.2",
|
|
"defu@>=6 <6.1.5": "^6.1.5",
|
|
"fast-uri@<3.1.2": "^3.1.2",
|
|
"js-cookie@<3.0.8": "3.0.8",
|
|
"tmp@<0.2.7": "0.2.7",
|
|
"brace-expansion@<1.1.13": "1.1.13",
|
|
"brace-expansion@>=2 <2.0.3": "2.0.3",
|
|
"brace-expansion@>=5 <5.0.6": "5.0.6",
|
|
"@jsonhero/json-infer-types>ip-address": "^10.2.0",
|
|
"@modelcontextprotocol/sdk@>=1.26.0>express-rate-limit": "^8.6.0"
|
|
},
|
|
"onlyBuiltDependencies": [
|
|
"@depot/cli",
|
|
"@fingerprintjs/fingerprintjs-pro-react",
|
|
"@prisma/client",
|
|
"@prisma/engines",
|
|
"@sentry/cli",
|
|
"@swc/core",
|
|
"better-sqlite3",
|
|
"cpu-features",
|
|
"esbuild",
|
|
"prisma",
|
|
"protobufjs",
|
|
"sharp",
|
|
"ssh2",
|
|
"turbo"
|
|
]
|
|
}
|
|
}
|