@preventive/triage 1.0.0-alpha.4 → 1.0.0-alpha.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/README.md +16 -0
  3. package/api/reap.ts +17 -0
  4. package/cli.js +6 -0
  5. package/client/dependency-paths.d.ts +2 -0
  6. package/client/dependency-paths.js +24 -0
  7. package/client/finding-link.js +305 -0
  8. package/client/linked-findings.d.ts +1 -0
  9. package/client/linked-findings.js +112 -0
  10. package/common/bundle-commit.d.ts +1 -0
  11. package/common/bundle-commit.js +4 -0
  12. package/common/bundle-compare-diff.d.ts +3 -0
  13. package/common/bundle-compare-diff.js +522 -0
  14. package/common/bundle-compare-inputs.d.ts +2 -0
  15. package/common/bundle-compare-inputs.js +65 -0
  16. package/common/bundle-metadata.d.ts +14 -0
  17. package/common/bundle-metadata.js +226 -0
  18. package/common/bundle-pkg-of.d.ts +1 -0
  19. package/common/bundle-pkg-of.js +90 -0
  20. package/common/bundle-reasons.d.ts +2 -0
  21. package/common/bundle-reasons.js +21 -0
  22. package/common/bundle-source-links.d.ts +6 -0
  23. package/common/bundle-source-links.js +216 -0
  24. package/common/bundle-sourcemap.d.ts +10 -0
  25. package/common/bundle-sourcemap.js +100 -0
  26. package/common/bundle-sources.d.ts +7 -0
  27. package/common/bundle-sources.js +310 -0
  28. package/common/bundle-stats.js +41 -0
  29. package/common/bundle-tabs.js +1 -0
  30. package/common/code-language.js +65 -0
  31. package/common/default-scan-models.ts +31 -0
  32. package/common/dependency-reviews.d.ts +23 -0
  33. package/common/dependency-reviews.js +81 -0
  34. package/common/finding-duplicates.js +58 -0
  35. package/common/finding-filters.js +274 -0
  36. package/common/finding-id.js +47 -0
  37. package/common/github-issue-labels.d.ts +1 -0
  38. package/common/github-issue-labels.js +11 -0
  39. package/common/github-pr.ts +56 -0
  40. package/common/gzip.js +59 -0
  41. package/common/line-diff.d.ts +8 -0
  42. package/common/line-diff.js +56 -0
  43. package/common/linked-workspace-groups.js +63 -0
  44. package/common/managed/comments.ts +34 -0
  45. package/common/managed/npm-packages.d.ts +5 -0
  46. package/common/managed/npm-packages.js +34 -0
  47. package/common/managed/permissions.ts +31 -0
  48. package/common/managed/report-content.ts +72 -0
  49. package/common/managed/report-filter.ts +108 -0
  50. package/common/managed/repository-alias.ts +65 -0
  51. package/common/managed/roles.ts +28 -0
  52. package/common/managed/routes.d.ts +2 -0
  53. package/common/managed/routes.js +254 -0
  54. package/common/managed/scan-models.ts +6 -0
  55. package/common/managed/team-app.d.ts +2 -0
  56. package/common/managed/team-app.js +31 -0
  57. package/common/managed/triage.ts +83 -0
  58. package/common/managed/upload-seal.ts +126 -0
  59. package/common/pretty-print.d.ts +5 -0
  60. package/common/pretty-print.js +24 -0
  61. package/common/revalidation-conflicts.js +48 -0
  62. package/common/revalidation-input-groups.js +56 -0
  63. package/common/save-error-reason.ts +20 -7
  64. package/common/scan-server.ts +13 -0
  65. package/common/server-info.ts +35 -0
  66. package/common/utf8.d.ts +3 -0
  67. package/common/utf8.js +45 -0
  68. package/common/workspace-app.js +23 -0
  69. package/common/workspace-groups.js +176 -0
  70. package/out/brotli-fallback.js +3 -3
  71. package/out/chunk-2AFQKRAM.js +51 -0
  72. package/out/chunk-2KBF5TZ7.js +2 -0
  73. package/out/chunk-2XUEZHIP.js +28 -0
  74. package/out/chunk-35VE6CDH.js +2 -0
  75. package/out/chunk-3BZ7JERB.js +1 -0
  76. package/out/chunk-4F6JSF57.js +3 -0
  77. package/out/chunk-4XCDFGDP.js +10 -0
  78. package/out/chunk-C4Z6ZBXV.js +10 -0
  79. package/out/chunk-CMQA4DM6.js +1 -0
  80. package/out/chunk-CVVJAXVY.js +2 -0
  81. package/out/chunk-DIJJNTRH.js +10 -0
  82. package/out/chunk-GJ4IUCNS.js +1 -0
  83. package/out/chunk-GNXOKIGP.js +80 -0
  84. package/out/chunk-GZ4MS3TE.js +13 -0
  85. package/out/chunk-IU3CCINR.js +3 -0
  86. package/out/chunk-K3X5EJBQ.js +2 -0
  87. package/out/chunk-KEX2ATEW.js +2 -0
  88. package/out/chunk-KVEUXIEU.js +1 -0
  89. package/out/chunk-KYCNJVB7.js +1 -0
  90. package/out/chunk-NRPC74AM.js +1 -0
  91. package/out/chunk-OOJM4CTU.js +1 -0
  92. package/out/chunk-PNHBZGDD.js +1 -0
  93. package/out/chunk-QZ3A376W.js +1 -0
  94. package/out/chunk-R5O6FAW4.js +2 -0
  95. package/out/chunk-RFZQQXSG.js +17 -0
  96. package/out/chunk-RSHMVYNS.js +1 -0
  97. package/out/chunk-TUII34K2.js +1 -0
  98. package/out/chunk-TY4RZD3Z.js +226 -0
  99. package/out/chunk-TZ5KB2Y5.js +10 -0
  100. package/out/chunk-U6OPEXIA.js +1 -0
  101. package/out/chunk-UNTRGE26.js +2 -0
  102. package/out/chunk-UXNUAIIC.js +33 -0
  103. package/out/chunk-UYAXVRVL.js +1 -0
  104. package/out/chunk-UZAZM2SX.js +2 -0
  105. package/out/chunk-V3ME6FI5.js +2 -0
  106. package/out/chunk-V5O7M2YT.js +1 -0
  107. package/out/chunk-VIHGO77Y.js +1 -0
  108. package/out/chunk-VQJCXGIY.js +7 -0
  109. package/out/chunk-W4J3MGLB.js +1 -0
  110. package/out/chunk-WGG7KQUH.js +1 -0
  111. package/out/chunk-WZDTZE64.js +1 -0
  112. package/out/chunk-XL35ZV6R.js +1 -0
  113. package/out/chunk-XYFXXB2G.js +10 -0
  114. package/out/chunk-YVZH5SHP.js +1 -0
  115. package/out/chunk-ZBBOBQ43.js +1 -0
  116. package/out/chunk-ZMQQCGF6.js +2 -0
  117. package/out/client-managed-import.js +4 -0
  118. package/out/client-managed.js +37 -0
  119. package/out/client-sync.js +10 -13
  120. package/out/github-issue-authorized.html +3 -0
  121. package/out/github-issue-authorized.js +1 -0
  122. package/out/graph.js +17 -4
  123. package/out/index.html +90 -8
  124. package/out/prism.js +6 -2
  125. package/out/stasis.svg +20 -0
  126. package/out/terminal.js +365 -39
  127. package/out/view.css +1 -1
  128. package/out/view.js +83 -117
  129. package/package.json +298 -59
  130. package/risk/files/characters.js +196 -0
  131. package/risk/files/hosts.js +205 -0
  132. package/risk/files/readability.js +171 -0
  133. package/risk/files/runs-code.js +595 -0
  134. package/risk/files/script.js +114 -0
  135. package/risk/files/stamps.js +46 -0
  136. package/risk/index.d.ts +156 -0
  137. package/risk/index.js +7 -0
  138. package/risk/npm/assess.js +223 -0
  139. package/risk/npm/combined.js +65 -0
  140. package/risk/npm/compare.js +228 -0
  141. package/risk/npm/dependencies.js +153 -0
  142. package/risk/npm/diff.js +43 -0
  143. package/risk/npm/downloads.js +84 -0
  144. package/risk/npm/files.js +34 -0
  145. package/risk/npm/held.js +60 -0
  146. package/risk/npm/install-scripts.js +34 -0
  147. package/risk/npm/malware.js +36 -0
  148. package/risk/npm/manifest.js +65 -0
  149. package/risk/npm/new-dependency.js +30 -0
  150. package/risk/npm/old-version.js +108 -0
  151. package/risk/npm/registry.js +282 -0
  152. package/risk/npm/release.js +85 -0
  153. package/risk/npm/score.js +59 -0
  154. package/risk/npm/specs.js +124 -0
  155. package/risk/npm/tarball.js +113 -0
  156. package/server-common/database-config.ts +16 -0
  157. package/server-common/initialize.ts +18 -0
  158. package/server-common/npm-advisories.ts +101 -0
  159. package/{server → server-common}/origin.ts +5 -5
  160. package/server-common/reap.ts +48 -0
  161. package/server-common/scan-config.ts +19 -0
  162. package/server-common/standalone.ts +29 -0
  163. package/server-common/storage-crypto.ts +182 -0
  164. package/server-common/storage-log.ts +38 -0
  165. package/server-common/vercel-blob.ts +103 -0
  166. package/server-e2e/app.ts +486 -0
  167. package/{server → server-e2e}/auth.ts +5 -1
  168. package/{server → server-e2e}/bus-receiver.ts +2 -1
  169. package/{server → server-e2e}/cli.js +9 -4
  170. package/{server → server-e2e}/config.ts +56 -35
  171. package/{server → server-e2e}/db-neon.ts +2 -2
  172. package/{server → server-e2e}/db-revision-sql.ts +1 -1
  173. package/{server → server-e2e}/db-stmt.ts +2 -2
  174. package/{server → server-e2e}/db.ts +7 -7
  175. package/{server → server-e2e}/http.ts +116 -18
  176. package/{server → server-e2e}/hub.ts +40 -9
  177. package/server-e2e/index.ts +17 -0
  178. package/server-e2e/lifecycle.ts +95 -0
  179. package/{server → server-e2e}/neon-driver.ts +2 -2
  180. package/{server → server-e2e}/npm-proxy.ts +12 -145
  181. package/{server → server-e2e}/objstore/blob-vercel.ts +10 -114
  182. package/{server → server-e2e}/objstore/blob.ts +1 -1
  183. package/server-e2e/objstore/fetch-mint-guard.ts +74 -0
  184. package/{server → server-e2e}/objstore/handlers.ts +9 -8
  185. package/{server → server-e2e}/objstore/init.ts +35 -18
  186. package/{server → server-e2e}/objstore/reaper.ts +1 -1
  187. package/server-e2e/objstore/rest-deny.ts +28 -0
  188. package/server-e2e/objstore/rest-mint.ts +224 -0
  189. package/{server → server-e2e}/objstore/rest.ts +27 -26
  190. package/{server → server-e2e}/objstore/sign.ts +105 -0
  191. package/{server → server-e2e}/objstore/store-neon.ts +6 -10
  192. package/{server → server-e2e}/objstore/store.ts +7 -7
  193. package/{server → server-e2e}/objstore/tokens.ts +2 -2
  194. package/{server → server-e2e}/pubsub.ts +12 -9
  195. package/{server → server-e2e}/sign.ts +4 -4
  196. package/{server → server-e2e}/sse-server.ts +102 -68
  197. package/{server → server-e2e}/sse-session.ts +28 -8
  198. package/{server → server-e2e}/static.ts +31 -21
  199. package/server-e2e/sync-handlers.ts +408 -0
  200. package/{server → server-e2e}/ws-server.ts +20 -9
  201. package/server-managed/activity.ts +245 -0
  202. package/server-managed/avatar-store.ts +12 -0
  203. package/server-managed/blob-store.ts +17 -0
  204. package/server-managed/brotli.ts +10 -0
  205. package/server-managed/bundle-advisories.ts +120 -0
  206. package/server-managed/bundle-advisory-inventory.ts +77 -0
  207. package/server-managed/bundle-build-diagnostics.d.ts +10 -0
  208. package/server-managed/bundle-build-diagnostics.js +23 -0
  209. package/server-managed/bundle-build-leases.ts +41 -0
  210. package/server-managed/bundle-build-worker.js +111 -0
  211. package/server-managed/bundle-build.ts +198 -0
  212. package/server-managed/bundle-cache.ts +199 -0
  213. package/server-managed/bundle-catalog.ts +29 -0
  214. package/server-managed/bundle-commits.ts +140 -0
  215. package/server-managed/bundle-store.ts +24 -0
  216. package/server-managed/bundle-summary-cache.ts +130 -0
  217. package/server-managed/bundle.ts +120 -0
  218. package/server-managed/cache-storage.ts +19 -0
  219. package/server-managed/cli.js +21 -0
  220. package/server-managed/combined.ts +46 -0
  221. package/server-managed/comments.ts +151 -0
  222. package/server-managed/config.ts +158 -0
  223. package/server-managed/content-access.ts +15 -0
  224. package/server-managed/crypto.ts +25 -0
  225. package/server-managed/db-methods.ts +1725 -0
  226. package/server-managed/db-neon.ts +367 -0
  227. package/server-managed/db-schema.ts +227 -0
  228. package/server-managed/db-table-names.ts +22 -0
  229. package/server-managed/db.ts +132 -0
  230. package/server-managed/dependency-reviews.ts +248 -0
  231. package/server-managed/github-app.ts +391 -0
  232. package/server-managed/github-errors.ts +68 -0
  233. package/server-managed/github-issue-links.ts +127 -0
  234. package/server-managed/github-issue-oauth.ts +47 -0
  235. package/server-managed/github-issues.ts +152 -0
  236. package/server-managed/github-metadata.ts +213 -0
  237. package/server-managed/github-oauth.ts +235 -0
  238. package/server-managed/github-pulls.ts +227 -0
  239. package/server-managed/http-response.ts +50 -0
  240. package/server-managed/http.ts +3472 -0
  241. package/server-managed/import-triage.ts +51 -0
  242. package/server-managed/index.ts +187 -0
  243. package/server-managed/kept.ts +28 -0
  244. package/server-managed/link-reports.ts +158 -0
  245. package/server-managed/locked-packages.ts +85 -0
  246. package/server-managed/lru.ts +30 -0
  247. package/server-managed/managed-issues.ts +75 -0
  248. package/server-managed/management-catalog.ts +45 -0
  249. package/server-managed/management.ts +99 -0
  250. package/server-managed/npm-insights.ts +440 -0
  251. package/server-managed/npm-kept.ts +65 -0
  252. package/server-managed/npm-loads.ts +167 -0
  253. package/server-managed/npm-manifest.ts +115 -0
  254. package/server-managed/npm-packages.ts +378 -0
  255. package/server-managed/npm-provenance.ts +123 -0
  256. package/server-managed/npm-risk.ts +177 -0
  257. package/server-managed/object-storage-disk.ts +148 -0
  258. package/server-managed/object-storage-vercel.ts +67 -0
  259. package/server-managed/object-storage.ts +74 -0
  260. package/server-managed/package-entry-points.ts +51 -0
  261. package/server-managed/pretty-print.ts +352 -0
  262. package/server-managed/public-workspace.ts +220 -0
  263. package/server-managed/rename-diff-worker.js +26 -0
  264. package/server-managed/rename-diff.ts +270 -0
  265. package/server-managed/repo-path.ts +21 -0
  266. package/server-managed/report-migration.ts +35 -0
  267. package/server-managed/report-query.ts +10 -0
  268. package/server-managed/report-response.ts +16 -0
  269. package/server-managed/report-sources.ts +178 -0
  270. package/server-managed/report-store.ts +47 -0
  271. package/server-managed/repository-aliases.ts +91 -0
  272. package/server-managed/repository-browser.ts +153 -0
  273. package/server-managed/repository-discovery.ts +82 -0
  274. package/server-managed/repository-policy.ts +27 -0
  275. package/server-managed/repository-risk.ts +420 -0
  276. package/server-managed/revisions.ts +35 -0
  277. package/server-managed/session.ts +92 -0
  278. package/server-managed/slugs.ts +38 -0
  279. package/server-managed/solidity-entry-points.ts +69 -0
  280. package/server-managed/sql-postgres.ts +31 -0
  281. package/server-managed/sql.ts +76 -0
  282. package/server-managed/static.ts +28 -0
  283. package/server-managed/storage-command.ts +16 -0
  284. package/server-managed/storage-db.ts +175 -0
  285. package/server-managed/storage-encryption.ts +279 -0
  286. package/server-managed/storage-maintenance.ts +256 -0
  287. package/server-managed/storage-payload.ts +103 -0
  288. package/server-managed/storage-stores.ts +85 -0
  289. package/server-managed/storage.ts +78 -0
  290. package/server-managed/team-app.ts +94 -0
  291. package/server-managed/team-catalog.ts +7 -0
  292. package/server-managed/team-feed.ts +173 -0
  293. package/server-managed/team-npm-scopes.ts +77 -0
  294. package/server-managed/team-reports.ts +228 -0
  295. package/server-managed/triage-response.ts +16 -0
  296. package/server-managed/type-only-worker.js +19 -0
  297. package/server-managed/type-only.ts +103 -0
  298. package/server-managed/uploads.ts +86 -0
  299. package/server-managed/upstream-cache.ts +215 -0
  300. package/server-managed/view-as.ts +58 -0
  301. package/server-managed/workspace-shares.ts +166 -0
  302. package/server.ts +50 -0
  303. package/server/index.ts +0 -477
  304. package/server/lifecycle.ts +0 -204
  305. package/server/sync-handlers.ts +0 -320
  306. /package/{LICENSE → LICENSE-MIT} +0 -0
  307. /package/{server → server-e2e}/config.example.json +0 -0
  308. /package/{server → server-e2e}/objstore/blob-fs.ts +0 -0
  309. /package/{server → server-e2e}/objstore/fs.ts +0 -0
  310. /package/{server → server-e2e}/peer.ts +0 -0
  311. /package/{server → server-e2e}/util.ts +0 -0
  312. /package/{server → server-e2e}/validation.ts +0 -0
@@ -0,0 +1,158 @@
1
+ // Managed-server boot config. GitHub user authorization establishes identity;
2
+ // optional App installation credentials enable repository access. A single
3
+ // GitHub App can provide both flows, with repository grants at installation.
4
+ import { env } from 'node:process'
5
+ import { databaseUrls } from '../server-common/database-config.ts'
6
+ import { MAX_UPLOAD_BYTES } from './uploads.ts'
7
+ import { parseStorageKey } from '../server-common/storage-crypto.ts'
8
+ import { defaultCacheDir } from '@preventive/upstream/npm.js'
9
+ import { RESERVED_COOKIES } from './session.ts'
10
+
11
+ const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost'])
12
+
13
+ export interface ManagedConfig {
14
+ port: number
15
+ host: string
16
+ dbPath: string
17
+ neonUrl?: string | null
18
+ blobToken?: string | null
19
+ storageEncryptionKey?: string | null
20
+ storageEncryptionMigrate?: boolean
21
+ storageEncryptionMigrateMaxMs?: number
22
+ vercelPreview?: boolean
23
+ serverless?: boolean
24
+ // Upstream's disk cache (setCacheDir) for audits and bundle builds: its default
25
+ // location, or null for none, as on Vercel, where the database keeps listings.
26
+ upstreamCacheDir?: string | null
27
+ debug: boolean
28
+ allowShare: boolean
29
+ trustProxyEnv: string | undefined
30
+ // GitHub App user-authorization (identity) credentials — the App's client id
31
+ // + secret, used by the login flow.
32
+ githubClientId: string
33
+ githubClientSecret: string
34
+ // Preapproved GitHub identity promoted on login when it is the only user
35
+ // and its role is No access, including a login after registration.
36
+ initialAdminGithubId: number | null
37
+ // Absolute callback registered with GitHub, e.g.
38
+ // 'https://triage.example.com/api/oauth/github/callback'.
39
+ oauthCallbackUrl: string
40
+ // Whether cookies carry `Secure` (callback is https). Also gates the
41
+ // `__Host-` prefix, so loopback http dev can run with a plain cookie name.
42
+ cookieSecure: boolean
43
+ sessionCookieName: string
44
+ sessionTtlMs: number
45
+ // Optional installation credentials for the same GitHub App as login.
46
+ // Repository permissions are approved when connecting repositories.
47
+ githubAppId: string | null
48
+ githubAppPrivateKey: string | null
49
+ githubAppSlug: string | null
50
+ githubNewIssueLabels?: string
51
+ // Max accepted size (bytes) for an uploaded report on the "Manage reports"
52
+ // page, before storage compression (default 25 MiB).
53
+ maxReportBytes: number
54
+ // Max accepted size (bytes) for an uploaded bundle (sourcemap / stasis
55
+ // archive). Bundles run larger than reports, so a higher cap (default 200 MiB).
56
+ maxBundleBytes: number
57
+ // How many triage-trail events to keep per finding (managed_finding_triage_event):
58
+ // 0, the default, keeps everything — the trail is the record. An operator
59
+ // who would rather bound the store sets a positive count; older events of a
60
+ // finding are then trimmed as new ones land.
61
+ triageHistoryLimit: number
62
+ }
63
+
64
+ function fail(msg: string): never {
65
+ throw new Error(msg)
66
+ }
67
+
68
+ function requireStr(name: string): string {
69
+ const v = env[name]
70
+ if (v == null || v === '') fail(`Missing required env ${name} (managed mode).`)
71
+ return v
72
+ }
73
+
74
+ function intEnv(name: string, def: number, min: number, max: number): number {
75
+ const raw = env[name]
76
+ const n = raw == null ? def : Number(raw)
77
+ if (!Number.isSafeInteger(n) || n < min || n > max) {
78
+ fail(`Invalid ${name}: ${raw} — must be an integer in [${min}, ${max}].`)
79
+ }
80
+ return n
81
+ }
82
+
83
+ function optionalGithubId(name: string): number | null {
84
+ const raw = env[name]
85
+ if (raw == null || raw === '') return null
86
+ const id = Number(raw)
87
+ if (!/^[1-9]\d*$/u.test(raw) || !Number.isSafeInteger(id)) {
88
+ fail(`Invalid ${name}: must be a single positive numeric GitHub user ID.`)
89
+ }
90
+ return id
91
+ }
92
+
93
+ function urlOrFail(name: string, raw: string): URL {
94
+ try { return new URL(raw) } catch { fail(`${name} is not a valid URL: ${raw}`) }
95
+ }
96
+
97
+ export function loadManagedConfig({ combined = false } = {}): ManagedConfig {
98
+ const serverless = env['VERCEL'] === '1'
99
+ const neonUrl = databaseUrls({ combined }).managed
100
+ const blobToken = env['BLOB_READ_WRITE_TOKEN'] || null
101
+ const storageEncryptionKey = env['MANAGED_STORAGE_ENCRYPTION_KEY'] || null
102
+ parseStorageKey(storageEncryptionKey)
103
+ const storageEncryptionMigrate = env['MANAGED_STORAGE_ENCRYPTION_MIGRATE'] === '1'
104
+ if (storageEncryptionMigrate && !storageEncryptionKey) fail('MANAGED_STORAGE_ENCRYPTION_MIGRATE requires MANAGED_STORAGE_ENCRYPTION_KEY.')
105
+ if (serverless && !neonUrl) fail('Vercel managed mode requires DATABASE_URL or MANAGED_DATABASE_URL.')
106
+ if (neonUrl && !blobToken) fail('Managed Neon mode requires BLOB_READ_WRITE_TOKEN.')
107
+ const host = env['HOST'] ?? '127.0.0.1'
108
+ const oauthCallbackUrl = requireStr('OAUTH_CALLBACK_URL')
109
+ const callback = urlOrFail('OAUTH_CALLBACK_URL', oauthCallbackUrl)
110
+ const cookieSecure = callback.protocol === 'https:'
111
+ // A `__Host-` cookie mandates `Secure`, so any non-loopback bind must be
112
+ // HTTPS — fail fast (mirrors server-e2e's boot check). Loopback dev over
113
+ // http is allowed with a non-prefixed cookie name.
114
+ if ((serverless || !LOOPBACK_HOSTS.has(host)) && !cookieSecure) {
115
+ fail(`Vercel or non-loopback HOST=${host} requires an https OAUTH_CALLBACK_URL — the session cookie needs Secure.`)
116
+ }
117
+ const sessionCookieName = env['SESSION_COOKIE_NAME'] ?? '__Host-dvsid'
118
+ if (sessionCookieName.startsWith('__Host-') && !cookieSecure) {
119
+ fail(`SESSION_COOKIE_NAME=${sessionCookieName} uses the __Host- prefix but the callback is not https. Use a non-prefixed name for loopback http dev.`)
120
+ }
121
+ if (RESERVED_COOKIES.includes(sessionCookieName.replace(/^__Host-/u, ''))) {
122
+ fail(`SESSION_COOKIE_NAME=${sessionCookieName} is reserved for another managed cookie. Choose a different name.`)
123
+ }
124
+ return {
125
+ neonUrl, blobToken, serverless, storageEncryptionKey, storageEncryptionMigrate,
126
+ storageEncryptionMigrateMaxMs: intEnv('MANAGED_STORAGE_ENCRYPTION_MIGRATE_MAX_MS', 150_000, 1, serverless ? 240_000 : 3_600_000),
127
+ vercelPreview: env['VERCEL_ENV'] === 'preview',
128
+ upstreamCacheDir: serverless ? null : defaultCacheDir ?? null,
129
+ port: intEnv('PORT', 8765, 0, 65535),
130
+ host,
131
+ // DB_PATH belongs to e2e in a combined process. Keep the two stores apart.
132
+ dbPath: env['MANAGED_DB_PATH'] ?? (combined ? undefined : env['DB_PATH']) ?? 'server-managed/data/managed.db',
133
+ debug: env['DEBUG'] === '1' || env['DEBUG'] === 'true',
134
+ allowShare: env['DEEPVIEW_ALLOW_SHARE'] === '1',
135
+ trustProxyEnv: env['TRUST_PROXY'] ?? (serverless ? '1' : undefined),
136
+ githubClientId: requireStr('GITHUB_CLIENT_ID'),
137
+ githubClientSecret: requireStr('GITHUB_CLIENT_SECRET'),
138
+ initialAdminGithubId: optionalGithubId('MANAGED_INITIAL_ADMIN_GITHUB_ID'),
139
+ oauthCallbackUrl,
140
+ cookieSecure,
141
+ sessionCookieName,
142
+ sessionTtlMs: intEnv('SESSION_TTL_MS', 1_209_600_000, 60_000, 7_776_000_000),
143
+ githubAppId: env['GITHUB_APP_ID'] ?? null,
144
+ githubAppPrivateKey: normalizePem(env['GITHUB_APP_PRIVATE_KEY']),
145
+ githubAppSlug: env['GITHUB_APP_SLUG'] ?? null,
146
+ githubNewIssueLabels: env['GITHUB_NEW_ISSUE_LABELS'] ?? '',
147
+ maxReportBytes: intEnv('MAX_REPORT_BYTES', 26_214_400, 1, 104_857_600),
148
+ maxBundleBytes: intEnv('MAX_BUNDLE_BYTES', 209_715_200, 1, MAX_UPLOAD_BYTES),
149
+ triageHistoryLimit: intEnv('TRIAGE_HISTORY_LIMIT', 0, 0, 1_000_000_000),
150
+ }
151
+ }
152
+
153
+ // PEM private keys are awkward in env vars; accept a literal multi-line value or
154
+ // one with escaped newlines (`\n`). Empty/absent → null (private repos off).
155
+ function normalizePem(raw: string | undefined): string | null {
156
+ if (raw == null || raw === '') return null
157
+ return raw.includes('\\n') ? raw.replaceAll('\\n', '\n') : raw
158
+ }
@@ -0,0 +1,15 @@
1
+ import type { ManagedDb, StoredUser } from './db.ts'
2
+
3
+ export type RepoScope = { repoId: number; path: string | null }
4
+ type Content = { repoId: number | null; repoDirectory?: string }
5
+
6
+ // Read grants for each request: neither the manager role nor a cached page
7
+ // confers repository access. Bundles have repository scope; reports also have
8
+ // a directory scope. Unassigned content is only available to administrators.
9
+ export async function contentAccess(db: Pick<ManagedDb, 'listRepoScopesForUser'>, user: StoredUser) {
10
+ const scopes = user.role === 'admin' ? null : await db.listRepoScopesForUser(user.id)
11
+ const bundle = (item: Content) => scopes == null || scopes.some(scope => scope.repoId === item.repoId)
12
+ const report = (item: Content) => scopes == null || scopes.some(scope => scope.repoId === item.repoId
13
+ && (!scope.path || item.repoDirectory === scope.path || item.repoDirectory?.startsWith(scope.path + '/')))
14
+ return { scopes, bundle, report }
15
+ }
@@ -0,0 +1,25 @@
1
+ // Token helpers for the managed auth server. The raw session token lives ONLY
2
+ // in the cookie; the DB row id is its SHA-256 (see db.ts / session.ts), so a
3
+ // DB read can't reconstruct a live cookie. No HMAC pepper is needed — the
4
+ // tokens are 256-bit random, not guessable or reversible from their hash.
5
+ import { createHash, randomBytes, timingSafeEqual } from 'node:crypto'
6
+
7
+ // 256-bit URL-safe random token: the session cookie value and the OAuth CSRF
8
+ // `state` nonce.
9
+ export function randomToken(): string {
10
+ return randomBytes(32).toString('base64url')
11
+ }
12
+
13
+ // At-rest id for a session cookie token (stored as the managed_session PK).
14
+ export function hashToken(token: string): string {
15
+ return createHash('sha256').update(token, 'utf8').digest('base64url')
16
+ }
17
+
18
+ // Constant-time equality for CSRF / OAuth-state checks. A length mismatch
19
+ // short-circuits (the length of these fixed-width tokens isn't a secret).
20
+ export function safeEqual(a: string, b: string): boolean {
21
+ const ab = Buffer.from(a, 'utf8')
22
+ const bb = Buffer.from(b, 'utf8')
23
+ if (ab.length !== bb.length) return false
24
+ return timingSafeEqual(ab, bb)
25
+ }