@preventive/triage 1.0.0-alpha.5 → 1.0.0-alpha.50

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 (366) 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 +3 -0
  6. package/client/dependency-paths.js +36 -0
  7. package/client/finding-link.js +305 -0
  8. package/client/linked-findings.d.ts +1 -0
  9. package/client/linked-findings.js +115 -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 +357 -0
  14. package/common/bundle-compare-inputs.d.ts +2 -0
  15. package/common/bundle-compare-inputs.js +65 -0
  16. package/common/bundle-compare-versions.js +172 -0
  17. package/common/bundle-comparison.d.ts +8 -0
  18. package/common/bundle-comparison.js +62 -0
  19. package/common/bundle-metadata.d.ts +15 -0
  20. package/common/bundle-metadata.js +226 -0
  21. package/common/bundle-pkg-of.d.ts +1 -0
  22. package/common/bundle-pkg-of.js +90 -0
  23. package/common/bundle-reasons.d.ts +2 -0
  24. package/common/bundle-reasons.js +21 -0
  25. package/common/bundle-source-links.d.ts +6 -0
  26. package/common/bundle-source-links.js +216 -0
  27. package/common/bundle-sourcemap.d.ts +10 -0
  28. package/common/bundle-sourcemap.js +100 -0
  29. package/common/bundle-sources.d.ts +7 -0
  30. package/common/bundle-sources.js +333 -0
  31. package/common/bundle-stats.js +41 -0
  32. package/common/bundle-tabs.js +1 -0
  33. package/common/code-language.js +65 -0
  34. package/common/default-scan-models.ts +31 -0
  35. package/common/dependency-reviews.d.ts +18 -0
  36. package/common/dependency-reviews.js +64 -0
  37. package/common/finding-duplicates.js +58 -0
  38. package/common/finding-filters.js +274 -0
  39. package/common/finding-id.js +47 -0
  40. package/common/github-issue-labels.d.ts +1 -0
  41. package/common/github-issue-labels.js +11 -0
  42. package/common/github-pr.ts +56 -0
  43. package/common/gzip.js +59 -0
  44. package/common/line-diff.d.ts +8 -0
  45. package/common/line-diff.js +57 -0
  46. package/common/linked-workspace-groups.js +63 -0
  47. package/common/managed/agent-tasks.ts +292 -0
  48. package/common/managed/comments.ts +34 -0
  49. package/common/managed/finding-detail.d.ts +2 -0
  50. package/common/managed/finding-detail.js +32 -0
  51. package/common/managed/github-agents.ts +114 -0
  52. package/common/managed/github-logins.ts +49 -0
  53. package/common/managed/npm-packages.d.ts +5 -0
  54. package/common/managed/npm-packages.js +34 -0
  55. package/common/managed/permissions.ts +31 -0
  56. package/common/managed/report-content.ts +72 -0
  57. package/common/managed/report-filter.ts +109 -0
  58. package/common/managed/repository-alias.ts +65 -0
  59. package/common/managed/roles.ts +35 -0
  60. package/common/managed/routes.d.ts +2 -0
  61. package/common/managed/routes.js +347 -0
  62. package/common/managed/scan-models.ts +6 -0
  63. package/common/managed/team-app.d.ts +5 -0
  64. package/common/managed/team-app.js +31 -0
  65. package/common/managed/triage.ts +83 -0
  66. package/common/managed/upload-seal.ts +126 -0
  67. package/common/pretty-print.d.ts +5 -0
  68. package/common/pretty-print.js +24 -0
  69. package/common/revalidation-conflicts.js +48 -0
  70. package/common/revalidation-input-groups.js +56 -0
  71. package/common/save-error-reason.ts +20 -7
  72. package/common/scan-server.ts +13 -0
  73. package/common/security-links.d.ts +5 -0
  74. package/common/security-links.js +33 -0
  75. package/common/server-info.ts +46 -0
  76. package/common/utf8.d.ts +3 -0
  77. package/common/utf8.js +45 -0
  78. package/common/workspace-app.js +39 -0
  79. package/common/workspace-groups.js +176 -0
  80. package/out/activity.js +4 -0
  81. package/out/brotli-fallback.js +3 -3
  82. package/out/chunk-2WDZPXVL.js +3 -0
  83. package/out/chunk-2XUEZHIP.js +28 -0
  84. package/out/chunk-2ZDZWLFY.js +1 -0
  85. package/out/chunk-4HI2MSAV.js +17 -0
  86. package/out/chunk-4XMP7YVH.js +36 -0
  87. package/out/chunk-6LNQTKAV.js +52 -0
  88. package/out/chunk-6S2U2UYG.js +232 -0
  89. package/out/chunk-75YLHBCM.js +3 -0
  90. package/out/chunk-7BKKYVWR.js +7 -0
  91. package/out/chunk-7C7DWL7I.js +1 -0
  92. package/out/chunk-7VAWINQF.js +6 -0
  93. package/out/chunk-C4Z6ZBXV.js +10 -0
  94. package/out/chunk-FC65TPUB.js +1 -0
  95. package/out/chunk-FKBXNUW4.js +1 -0
  96. package/out/chunk-GDPCLSOS.js +2 -0
  97. package/out/chunk-GO7FT4OD.js +1 -0
  98. package/out/chunk-HTBYHSPG.js +1 -0
  99. package/out/chunk-IYPY47MC.js +1 -0
  100. package/out/chunk-IZ7POWI3.js +18 -0
  101. package/out/chunk-JCIIBLKR.js +1 -0
  102. package/out/chunk-NRPC74AM.js +1 -0
  103. package/out/chunk-OOJM4CTU.js +1 -0
  104. package/out/chunk-PBZPYIG7.js +1 -0
  105. package/out/chunk-PNHBZGDD.js +1 -0
  106. package/out/chunk-PWFUOLZK.js +17 -0
  107. package/out/chunk-PXK7EU4V.js +1 -0
  108. package/out/chunk-QB4CBOX2.js +1 -0
  109. package/out/chunk-R3NZFXVR.js +1 -0
  110. package/out/chunk-R5O6FAW4.js +2 -0
  111. package/out/chunk-SXV2T5C4.js +80 -0
  112. package/out/chunk-TQJDYHVE.js +2 -0
  113. package/out/chunk-TUII34K2.js +1 -0
  114. package/out/chunk-UK7RGJT6.js +3 -0
  115. package/out/chunk-VFWMQ3LG.js +2 -0
  116. package/out/chunk-VH4XMZIW.js +2 -0
  117. package/out/chunk-WCPJDAYA.js +10 -0
  118. package/out/chunk-WFYLMF6Z.js +10 -0
  119. package/out/chunk-WYLQWYLA.js +1 -0
  120. package/out/client-managed-import.js +4 -0
  121. package/out/client-managed.js +33 -0
  122. package/out/client-sync.js +11 -17
  123. package/out/compare.js +35 -0
  124. package/out/dialogs.js +55 -0
  125. package/out/github-issue-authorized.html +3 -0
  126. package/out/github-issue-authorized.js +1 -0
  127. package/out/graph.js +17 -4
  128. package/out/index.html +90 -8
  129. package/out/prism.js +14 -10
  130. package/out/scan.js +2 -0
  131. package/out/stasis.svg +20 -0
  132. package/out/terminal.js +366 -39
  133. package/out/view.css +1 -1
  134. package/out/view.js +19 -144
  135. package/package.json +362 -59
  136. package/risk/actions/findings.js +161 -0
  137. package/risk/actions/resolve.js +219 -0
  138. package/risk/actions/scripts/commands.js +182 -0
  139. package/risk/actions/scripts/grade.js +113 -0
  140. package/risk/actions/scripts/packages.js +123 -0
  141. package/risk/actions/scripts/shell.js +169 -0
  142. package/risk/actions/uses.js +76 -0
  143. package/risk/actions/workflow.js +110 -0
  144. package/risk/files/characters.js +196 -0
  145. package/risk/files/hosts.js +205 -0
  146. package/risk/files/readability.js +177 -0
  147. package/risk/files/runs-code.js +615 -0
  148. package/risk/files/script.js +118 -0
  149. package/risk/files/stamps.js +47 -0
  150. package/risk/index.d.ts +254 -0
  151. package/risk/index.js +11 -0
  152. package/risk/npm/assess.js +285 -0
  153. package/risk/npm/combined.js +68 -0
  154. package/risk/npm/compare.js +257 -0
  155. package/risk/npm/dependencies.js +172 -0
  156. package/risk/npm/diff.js +79 -0
  157. package/risk/npm/downloads.js +84 -0
  158. package/risk/npm/files.js +445 -0
  159. package/risk/npm/held.js +63 -0
  160. package/risk/npm/install-scripts.js +34 -0
  161. package/risk/npm/malware.js +36 -0
  162. package/risk/npm/manifest.js +65 -0
  163. package/risk/npm/new-dependency.js +60 -0
  164. package/risk/npm/old-version.js +108 -0
  165. package/risk/npm/registry.js +388 -0
  166. package/risk/npm/release.js +85 -0
  167. package/risk/npm/score.js +59 -0
  168. package/risk/npm/specs.js +124 -0
  169. package/risk/npm/tarball.js +96 -0
  170. package/server-common/database-config.ts +16 -0
  171. package/server-common/initialize.ts +18 -0
  172. package/server-common/npm-advisories.ts +101 -0
  173. package/{server → server-common}/origin.ts +5 -5
  174. package/server-common/reap.ts +48 -0
  175. package/server-common/scan-config.ts +19 -0
  176. package/server-common/standalone.ts +29 -0
  177. package/server-common/storage-crypto.ts +182 -0
  178. package/server-common/storage-log.ts +38 -0
  179. package/server-common/vercel-blob.ts +103 -0
  180. package/server-e2e/app.ts +487 -0
  181. package/{server → server-e2e}/bus-receiver.ts +2 -1
  182. package/{server → server-e2e}/cli.js +9 -4
  183. package/{server → server-e2e}/config.ts +50 -42
  184. package/{server → server-e2e}/db-neon.ts +2 -2
  185. package/{server → server-e2e}/db-revision-sql.ts +1 -1
  186. package/{server → server-e2e}/db-stmt.ts +2 -2
  187. package/{server → server-e2e}/db.ts +7 -7
  188. package/{server → server-e2e}/http.ts +44 -13
  189. package/{server → server-e2e}/hub.ts +40 -9
  190. package/server-e2e/index.ts +17 -0
  191. package/server-e2e/lifecycle.ts +95 -0
  192. package/{server → server-e2e}/neon-driver.ts +2 -2
  193. package/{server → server-e2e}/npm-proxy.ts +12 -145
  194. package/{server → server-e2e}/objstore/blob-vercel.ts +10 -114
  195. package/{server → server-e2e}/objstore/blob.ts +1 -1
  196. package/server-e2e/objstore/fetch-mint-guard.ts +74 -0
  197. package/{server → server-e2e}/objstore/handlers.ts +9 -8
  198. package/{server → server-e2e}/objstore/init.ts +19 -27
  199. package/{server → server-e2e}/objstore/reaper.ts +1 -1
  200. package/server-e2e/objstore/rest-deny.ts +28 -0
  201. package/server-e2e/objstore/rest-mint.ts +224 -0
  202. package/{server → server-e2e}/objstore/rest.ts +3 -3
  203. package/{server → server-e2e}/objstore/store-neon.ts +6 -10
  204. package/{server → server-e2e}/objstore/store.ts +7 -7
  205. package/{server → server-e2e}/objstore/tokens.ts +2 -2
  206. package/{server → server-e2e}/pubsub.ts +12 -9
  207. package/{server → server-e2e}/sign.ts +4 -4
  208. package/{server → server-e2e}/sse-server.ts +60 -33
  209. package/{server → server-e2e}/static.ts +31 -21
  210. package/{server → server-e2e}/sync-handlers.ts +64 -30
  211. package/{server → server-e2e}/ws-server.ts +20 -9
  212. package/server-managed/activity.ts +258 -0
  213. package/server-managed/agent-merges.ts +109 -0
  214. package/server-managed/app-agents.ts +21 -0
  215. package/server-managed/avatar-store.ts +13 -0
  216. package/server-managed/blob-store.ts +17 -0
  217. package/server-managed/brotli.ts +10 -0
  218. package/server-managed/bundle-advisories.ts +120 -0
  219. package/server-managed/bundle-advisory-inventory.ts +77 -0
  220. package/server-managed/bundle-build-diagnostics.d.ts +10 -0
  221. package/server-managed/bundle-build-diagnostics.js +23 -0
  222. package/server-managed/bundle-build-leases.ts +41 -0
  223. package/server-managed/bundle-build-worker.js +115 -0
  224. package/server-managed/bundle-build.ts +200 -0
  225. package/server-managed/bundle-cache.ts +211 -0
  226. package/server-managed/bundle-catalog.ts +29 -0
  227. package/server-managed/bundle-commits.ts +140 -0
  228. package/server-managed/bundle-store.ts +24 -0
  229. package/server-managed/bundle-summary-cache.ts +130 -0
  230. package/server-managed/bundle.ts +119 -0
  231. package/server-managed/cache-storage.ts +20 -0
  232. package/server-managed/cli.js +21 -0
  233. package/server-managed/combined.ts +47 -0
  234. package/server-managed/comments.ts +151 -0
  235. package/server-managed/commit-coauthors.ts +106 -0
  236. package/server-managed/commit-lockfiles.ts +73 -0
  237. package/server-managed/comparison.ts +41 -0
  238. package/server-managed/config.ts +164 -0
  239. package/server-managed/content-access.ts +15 -0
  240. package/server-managed/crypto.ts +25 -0
  241. package/server-managed/db-methods.ts +1923 -0
  242. package/server-managed/db-neon.ts +458 -0
  243. package/server-managed/db-schema.ts +238 -0
  244. package/server-managed/db-table-names.ts +22 -0
  245. package/server-managed/db.ts +142 -0
  246. package/server-managed/dependabot-access.ts +60 -0
  247. package/server-managed/dependabot-alerts.ts +185 -0
  248. package/server-managed/dependency-review-comments.ts +100 -0
  249. package/server-managed/dependency-reviews.ts +346 -0
  250. package/server-managed/github-accounts.ts +60 -0
  251. package/server-managed/github-actions.ts +71 -0
  252. package/server-managed/github-app.ts +415 -0
  253. package/server-managed/github-errors.ts +70 -0
  254. package/server-managed/github-graphql.ts +39 -0
  255. package/server-managed/github-issue-links.ts +140 -0
  256. package/server-managed/github-issue-oauth.ts +47 -0
  257. package/server-managed/github-issues.ts +152 -0
  258. package/server-managed/github-link-oauth.ts +69 -0
  259. package/server-managed/github-links.ts +204 -0
  260. package/server-managed/github-metadata.ts +213 -0
  261. package/server-managed/github-oauth.ts +238 -0
  262. package/server-managed/github-pulls.ts +227 -0
  263. package/server-managed/github-requests.ts +39 -0
  264. package/server-managed/github-users.ts +30 -0
  265. package/server-managed/http-response.ts +76 -0
  266. package/server-managed/http.ts +5329 -0
  267. package/server-managed/import-triage.ts +51 -0
  268. package/server-managed/index.ts +192 -0
  269. package/server-managed/kept-briefly.ts +37 -0
  270. package/server-managed/kept.ts +28 -0
  271. package/server-managed/link-reports.ts +162 -0
  272. package/server-managed/locked-packages.ts +129 -0
  273. package/server-managed/lockfile-scope.ts +49 -0
  274. package/server-managed/lru.ts +30 -0
  275. package/server-managed/managed-issues.ts +78 -0
  276. package/server-managed/management-catalog.ts +45 -0
  277. package/server-managed/management.ts +99 -0
  278. package/server-managed/npm-delta-worker.js +23 -0
  279. package/server-managed/npm-delta.ts +194 -0
  280. package/server-managed/npm-insights.ts +449 -0
  281. package/server-managed/npm-kept.ts +215 -0
  282. package/server-managed/npm-loads.ts +264 -0
  283. package/server-managed/npm-manifest.ts +115 -0
  284. package/server-managed/npm-packages.ts +454 -0
  285. package/server-managed/npm-provenance.ts +123 -0
  286. package/server-managed/npm-risk.ts +285 -0
  287. package/server-managed/object-storage-disk.ts +148 -0
  288. package/server-managed/object-storage-vercel.ts +67 -0
  289. package/server-managed/object-storage.ts +75 -0
  290. package/server-managed/package-entry-points.ts +51 -0
  291. package/server-managed/pretty-print.ts +434 -0
  292. package/server-managed/public-workspace.ts +238 -0
  293. package/server-managed/pull-comment-reviews.ts +161 -0
  294. package/server-managed/pull-dependencies.ts +348 -0
  295. package/server-managed/pull-dependency-reviews.ts +328 -0
  296. package/server-managed/pull-reviews.ts +205 -0
  297. package/server-managed/queue.ts +39 -0
  298. package/server-managed/release-commits.ts +84 -0
  299. package/server-managed/rename-diff-worker.js +26 -0
  300. package/server-managed/rename-diff.ts +279 -0
  301. package/server-managed/repo-path.ts +21 -0
  302. package/server-managed/report-migration.ts +35 -0
  303. package/server-managed/report-query.ts +10 -0
  304. package/server-managed/report-response.ts +16 -0
  305. package/server-managed/report-sources.ts +178 -0
  306. package/server-managed/report-store.ts +85 -0
  307. package/server-managed/repository-actions.ts +218 -0
  308. package/server-managed/repository-agents.ts +355 -0
  309. package/server-managed/repository-aliases.ts +91 -0
  310. package/server-managed/repository-browser.ts +294 -0
  311. package/server-managed/repository-changes.ts +284 -0
  312. package/server-managed/repository-code.ts +215 -0
  313. package/server-managed/repository-dependency-reviews.ts +165 -0
  314. package/server-managed/repository-discovery.ts +82 -0
  315. package/server-managed/repository-history.ts +432 -0
  316. package/server-managed/repository-issues.ts +195 -0
  317. package/server-managed/repository-policy.ts +27 -0
  318. package/server-managed/repository-pulls.ts +156 -0
  319. package/server-managed/repository-pulse.ts +460 -0
  320. package/server-managed/repository-risk-assess.ts +105 -0
  321. package/server-managed/repository-risk-audit.ts +79 -0
  322. package/server-managed/repository-risk-read.ts +170 -0
  323. package/server-managed/repository-risk.ts +491 -0
  324. package/server-managed/repository-squashes.ts +168 -0
  325. package/server-managed/request-timing.ts +63 -0
  326. package/server-managed/reviewed-dependencies.ts +150 -0
  327. package/server-managed/revisions.ts +35 -0
  328. package/server-managed/scan-downloads.ts +87 -0
  329. package/server-managed/session.ts +132 -0
  330. package/server-managed/slugs.ts +38 -0
  331. package/server-managed/solidity-entry-points.ts +71 -0
  332. package/server-managed/sql-postgres.ts +34 -0
  333. package/server-managed/sql.ts +88 -0
  334. package/server-managed/static.ts +28 -0
  335. package/server-managed/storage-command.ts +16 -0
  336. package/server-managed/storage-db.ts +183 -0
  337. package/server-managed/storage-encryption.ts +290 -0
  338. package/server-managed/storage-maintenance.ts +259 -0
  339. package/server-managed/storage-payload.ts +121 -0
  340. package/server-managed/storage-stores.ts +91 -0
  341. package/server-managed/storage.ts +82 -0
  342. package/server-managed/team-app.ts +134 -0
  343. package/server-managed/team-catalog.ts +7 -0
  344. package/server-managed/team-feed.ts +180 -0
  345. package/server-managed/team-npm-scopes.ts +106 -0
  346. package/server-managed/team-reports.ts +307 -0
  347. package/server-managed/triage-response.ts +16 -0
  348. package/server-managed/type-only-worker.js +19 -0
  349. package/server-managed/type-only.ts +105 -0
  350. package/server-managed/uploads.ts +106 -0
  351. package/server-managed/upstream-cache.ts +243 -0
  352. package/server-managed/view-as.ts +58 -0
  353. package/server-managed/workspace-shares.ts +169 -0
  354. package/server.ts +70 -0
  355. package/server/index.ts +0 -480
  356. package/server/lifecycle.ts +0 -208
  357. /package/{LICENSE → LICENSE-MIT} +0 -0
  358. /package/{server → server-e2e}/auth.ts +0 -0
  359. /package/{server → server-e2e}/config.example.json +0 -0
  360. /package/{server → server-e2e}/objstore/blob-fs.ts +0 -0
  361. /package/{server → server-e2e}/objstore/fs.ts +0 -0
  362. /package/{server → server-e2e}/objstore/sign.ts +0 -0
  363. /package/{server → server-e2e}/peer.ts +0 -0
  364. /package/{server → server-e2e}/sse-session.ts +0 -0
  365. /package/{server → server-e2e}/util.ts +0 -0
  366. /package/{server → server-e2e}/validation.ts +0 -0
@@ -0,0 +1,118 @@
1
+ // How a script's text is read, for risk's readers of a file
2
+ // (readability.js, runs-code.js, hosts.js): JavaScript's and CSS's strings,
3
+ // regular expressions and comments, set apart from their code, what of their
4
+ // blanks a minifier drops, which files are scripts node runs, and each
5
+ // offset's line.
6
+
7
+ // Code minified into lines shorter than NPM_LONG_LINE: outside its strings and
8
+ // comments (`/* @__PURE__ */`), its lines average more than anyone writes and
9
+ // next to none of its spaces are ones a minifier drops. Told only in what
10
+ // minifiers write, as GitHub Linguist tells minified files only in JavaScript
11
+ // and CSS, each by its strings and comments (matched in one pass, so that
12
+ // neither starts inside the other, a comment with the spaces around it) and
13
+ // the spaces it can do without.
14
+ export const MINIFIED_AVERAGE = 110
15
+ const MINIFIED_SPACES = .01
16
+ // What a regular expression set aside holds.
17
+ const REGEX = '\uE000'
18
+ const JS = {
19
+ // A hashbang, the file's first line (`#!/usr/bin/env node`), a comment,
20
+ // after a byte order mark too, which node's ES module loader drops first.
21
+ // A string's or template's escape may be a line continuation (`\` before a
22
+ // line break, CRLF's whole).
23
+ // Linear on any text: a regular expression's context looked back on only
24
+ // before a `/`, a comment's leading blanks taken only from where they
25
+ // start, and what is left open read to where it would end, a string or a
26
+ // regular expression its line's end, a template or a comment the text's,
27
+ // rather than tried again from each later quote or slash.
28
+ // A regular expression first where a value starts (after `=>`, `...`, an operator or a keyword, not a property: `x.default/2`, `this.#in/2`), so a quote in it (`/["']/`) starts no string.
29
+ stringOrComment: /((?=\/)(?<=(?:^|\.\.\.|[(,=:[!&|?{};>+\-*/%^<~]|(?<![\p{ID_Continue}$.#])(?:await|case|default|delete|do|else|extends|in|instanceof|new|of|return|throw|typeof|void|yield))[ \t]*)\/(?![/*])(?:[^/\\[\n]|\\.|\[(?:[^\]\\\n]|\\.)*(?:\]|(?=\\?$)))+(?:\/[a-z]*|(?=\\?$))|"(?:[^"\\\n]|\\(?:\r\n|[\s\S]))*(?:"|$)|'(?:[^'\\\n]|\\(?:\r\n|[\s\S]))*(?:'|$)|`(?:[^`\\]|\\(?:\r\n|[\s\S]))*(?:`|(?![\s\S])))|(?<![ \t])[ \t]*(?:\/\*[\s\S]*?(?:\*\/|(?![\s\S]))|\/\/.*|(?<![\s\S])\uFEFF?#!.*)[ \t]*/gmu,
30
+ // Beside punctuation (`a, b`, `x = 1`), not between two words, as
31
+ // JavaScript tells a word's characters (`return a`, `var π`, `var \u03c0`),
32
+ droppable: /(?<![\p{ID_Continue}$\\\u200C\u200D])[ \t]+|[ \t]+(?![\p{ID_Continue}$\\\u200C\u200D])/gu,
33
+ // nor between two `+`, two `-` or two `/` (`a+ +b` is no `a++b`, `a/ /b/`
34
+ // no comment), nor after a regular expression before a word (`/a/ in b`).
35
+ needed: (code, index, run) => ('+-/'.includes(code[index - 1]) && code[index + run.length] === code[index - 1])
36
+ || (code[index - 2] === REGEX && /[\p{ID_Continue}$\\\u200C\u200D]/u.test(code[index + run.length])),
37
+ }
38
+ const CSS = {
39
+ // No `//` comments, so `url(https://…)` is code.
40
+ stringOrComment: /("(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*')|[ \t]*\/\*[\s\S]*?\*\/[ \t]*/gu,
41
+ // Beside braces, `;`, `,`, `>`, parentheses, after `:` (`a { color: red }`)
42
+ // and beside a selector's `+` or `~` (`.a + .b`), not those a selector or a
43
+ // value needs (`.a .b`, `1px solid #fff`, `a :hover`),
44
+ droppable: /(?<=[{};,:>(])[ \t]+|[ \t]+(?=[{};,>)!])|(?<=[+~])[ \t]+|[ \t]+(?=[+~])/gu,
45
+ // nor beside `+` or `~` in parentheses (`calc(1px + var(--x))`) or in a
46
+ // custom property's value, kept for a `calc()` to come (`--gap:1px + 2px`).
47
+ needed: (code, index, run, depth) => /[+~]/u.test(code[index - 1] + code[index + run.length])
48
+ && (depth > 0 || /^\s*--[^:]*:/u.test(code.slice(Math.max(...['{', ';', '}'].map(end => code.lastIndexOf(end, index))) + 1, index))),
49
+ }
50
+ // JavaScript's strings and regular expressions (the first group) and its
51
+ // comments, for a reader that sets them aside (runs-code.js, hosts.js).
52
+ export const JS_STRING_OR_COMMENT = JS.stringOrComment
53
+ // A stylesheet's by its name, else JavaScript's for a script by its name, or
54
+ // for any other where node runs it as one (`script`).
55
+ export const minifiable = (path, script = false) => /\.css$/iu.test(path) ? CSS : script || /\.[cm]?js$/iu.test(path) ? JS : null
56
+ // How much of a text is read for how its lines are minified: their average
57
+ // is told by as much of them, and a text of millions of lines is read no
58
+ // further.
59
+ const MINIFIED_READ = 4 * 1024 * 1024
60
+ // Its code's lines' average length where they are minified into short ones,
61
+ // else 0, told by its first MINIFIED_READ characters.
62
+ export function minifiedAverage(whole, { stringOrComment, droppable, needed }) {
63
+ const text = whole.length > MINIFIED_READ ? whole.slice(0, whole.lastIndexOf('\n', MINIFIED_READ) + 1 || MINIFIED_READ) : whole
64
+ // Its lines of code alone, as long as what is on them: a comment set aside
65
+ // leaves nothing but its line breaks, a string or template `""` and its, a
66
+ // regular expression `/REGEX/`, so the code either side stays apart.
67
+ const lines = text.replaceAll(stringOrComment, (match, string) => (string === undefined ? '' : string[0] === '/' ? `/${REGEX}/` : '""') + match.replaceAll(/[^\n]/gu, ''))
68
+ .replaceAll(/^[ \t]+/gmu, '')
69
+ .split('\n').filter(line => line.trim() !== '')
70
+ const code = lines.join('\n'), length = code.length - (lines.length - 1)
71
+ // Counted by character: a run aligning `=` is as many spaces as it is wide.
72
+ let depth = 0, dropped = 0, scanned = 0
73
+ for (const { 0: run, index } of code.matchAll(droppable)) {
74
+ for (; scanned < index; scanned++) depth = Math.max(0, depth + (code[scanned] === '(' ? 1 : code[scanned] === ')' ? -1 : 0))
75
+ if (!needed(code, index, run, depth)) dropped += run.length
76
+ }
77
+ return lines.length > 0 && length > MINIFIED_AVERAGE * lines.length && dropped < MINIFIED_SPACES * length ? Math.round(length / lines.length) : 0
78
+ }
79
+
80
+ // JavaScript or TypeScript (a declaration file too, which node runs as any
81
+ // other), or a script run by node by its hashbang (`bin/cli`, with no
82
+ // extension), a byte order mark before it or not, as node runs one after it
83
+ // as an ES module (in a `"type": "module"` package).
84
+ const SCRIPT = /\.[cm]?[jt]sx?$/iu
85
+ const NODE_HASHBANG = /^\uFEFF?#!.*\bnode\b/u
86
+ // Whether a file (its path and text) is a script, as SCRIPT and NODE_HASHBANG
87
+ // tell, or as node runs it whatever its name (`script`, npm/files.js
88
+ // npmLoadedScripts).
89
+ export const npmIsScript = (path, text, script = false) => typeof text === 'string' && (script || SCRIPT.test(path) || NODE_HASHBANG.test(text))
90
+ // How many places a file is told doing a thing, the first ones, at most
91
+ // (runs-code.js, hosts.js): a file doing it on millions of lines is told by
92
+ // its first, and what it holds of them kept to a bound however many there
93
+ // are.
94
+ export const NPM_MAX_FOUND = 100
95
+ // Every how many line breaks one is kept where lineOf counts from.
96
+ const LINE_MARK = 1024
97
+ // Each offset in a text as its line: one more than the line breaks before it,
98
+ // counted on from the nearest of every LINE_MARKth, which alone are kept.
99
+ export function lineOf(text) {
100
+ let marks = null
101
+ return offset => {
102
+ if (!marks) {
103
+ marks = []
104
+ for (let at = text.indexOf('\n'), count = 0; at !== -1; at = text.indexOf('\n', at + 1), count++) if (count % LINE_MARK === 0) marks.push(at)
105
+ }
106
+ let high = marks.length, low = 0
107
+ while (low < high) {
108
+ const middle = (low + high) >> 1
109
+ if (marks[middle] < offset) low = middle + 1
110
+ else high = middle
111
+ }
112
+ if (low === 0) return 1
113
+ let breaks = (low - 1) * LINE_MARK + 1
114
+ for (let at = text.indexOf('\n', marks[low - 1] + 1); at !== -1 && at < offset; at = text.indexOf('\n', at + 1)) breaks++
115
+ return breaks + 1
116
+ }
117
+ }
118
+
@@ -0,0 +1,47 @@
1
+ // A file's stamps: what risk reads of one file (the readers beside this), and
2
+ // what of it calls for a look, as a list of files tags it and orders it by:
3
+ // each `{ name, severity }`, its tag's text and how severe it is (SEVERITIES).
4
+ import { READABILITY, npmFileReadability, npmLabelMark, npmLargeCode, npmReadabilityLabels, npmTaggedCategory } from './readability.js'
5
+ import { npmExfiltration, npmHostSeverity, npmHostTags, npmHosts } from './hosts.js'
6
+ import { npmRunsCode, npmRunsCodeFlags } from './runs-code.js'
7
+
8
+ // How severe a stamp or a finding is, the most first: `critical` red,
9
+ // `high` a warning's orange, `moderate` yellow, `low`, and a `notice`, under
10
+ // a low one.
11
+ export const SEVERITIES = ['critical', 'high', 'moderate', 'low', 'notice']
12
+
13
+ // A label's mark (readability.js READABILITY) as a severity: a warning's
14
+ // orange, or a notice.
15
+ const severityOf = mark => mark === 'warn' ? 'high' : 'notice'
16
+
17
+ // A file's stamps, from what was read of it: what its code does beyond
18
+ // itself (runs-code.js npmRunsCodeFlags), a warning's or, for the whole
19
+ // environment read out and publishing, critical; the services it names
20
+ // (hosts.js), a stamp a tag, at the tag's severity (Exfiltration critical,
21
+ // Remote high, IP lookup moderate); a large code file, a notice; an
22
+ // obfuscator's fingerprint, a warning's; and its control characters, long
23
+ // lines and category, by their marks, but for what reads as most files do
24
+ // (UTF-8, ASCII) and a source map.
25
+ function npmStampsOf({ path, bytes, readability, runsCode, exfiltration, script }) {
26
+ const category = npmTaggedCategory(readability)
27
+ const listed = category !== 'ascii' && category !== 'map' && READABILITY.get(category).mark
28
+ return [...npmRunsCodeFlags(runsCode),
29
+ ...npmHostTags(exfiltration).map(([tag, services]) => ({ name: tag, severity: npmHostSeverity(tag, services) })),
30
+ ...npmLargeCode(path, bytes, script) ? [{ name: 'Large', severity: 'notice' }] : [],
31
+ ...readability.obfuscation ? [{ name: 'Obfuscated', severity: 'high' }] : [],
32
+ ...npmReadabilityLabels(readability).filter(label => label === 'controls' || label === 'long')
33
+ .map(label => ({ name: READABILITY.get(label).tag, severity: severityOf(npmLabelMark(readability, label)) })),
34
+ ...listed ? [{ name: READABILITY.get(category).tag, severity: severityOf(listed) }] : []]
35
+ }
36
+
37
+ // Everything risk reads of a file (`path`, its `text`, null for one that
38
+ // isn't, and its size in `bytes`): how it reads (npmFileReadability), what
39
+ // its code does beyond itself (npmRunsCode), the services it names under the
40
+ // hosts' tags (npmExfiltration) and the hosts its code names (npmHosts), and its
41
+ // `stamps` (npmStampsOf); read as a script where node runs it as one whatever
42
+ // its name (`script`, npm/files.js npmLoadedScripts).
43
+ export function npmFileRisk(path, text, bytes, script = false) {
44
+ const readability = npmFileReadability(path, text, script)
45
+ const exfiltration = npmExfiltration(path, text, script), runsCode = npmRunsCode(path, text, script)
46
+ return { readability, runsCode, exfiltration, hosts: npmHosts(path, text, script), stamps: npmStampsOf({ path, bytes, readability, runsCode, exfiltration, script }) }
47
+ }
@@ -0,0 +1,254 @@
1
+ // Hand-written against index.js; a change to either belongs with the other.
2
+ import type { CacheStore } from '@preventive/upstream/npm.js'
3
+
4
+ // How severe a stamp or a finding is, the most first: `critical` red, `high`
5
+ // a warning's orange, `moderate` yellow, `low`, and a `notice`.
6
+ export type Severity = 'critical' | 'high' | 'moderate' | 'low' | 'notice'
7
+
8
+ // The severities, the most severe first.
9
+ export const SEVERITIES: readonly Severity[]
10
+
11
+ // How old a version must be to be trusted unless said: two weeks, in ms.
12
+ export const DEFAULT_MINIMUM_RELEASE_AGE: number
13
+
14
+ // npm has no such package or version (`package-not-found`,
15
+ // `version-not-found`), a tarball unpacks past risk's bounds
16
+ // (`package-too-large`), npm asked to be asked later (`upstream-busy`, a 429,
17
+ // for a caller to wait on and ask again), or npm couldn't be asked
18
+ // (`upstream-unavailable`), answered past them (`upstream-too-large`) or
19
+ // answered what can't be read (`upstream-invalid`). `message` tells which
20
+ // request and why.
21
+ export class NpmRiskError extends Error {
22
+ code: string
23
+ }
24
+
25
+ export interface AssessNpmOptions {
26
+ // The version to compare with; true for the one of the package's versions
27
+ // most likely free of what an attack injects, as risk picks it by npm's
28
+ // document (risk/npm/old-version.js), none where none is below it; without
29
+ // one, everything of the version is new.
30
+ oldVersion?: string | true | null
31
+ // Compare all files with an empty package, for an initial dependency review.
32
+ fromEmpty?: boolean
33
+ // Younger than this, in ms, a version is `too-new`; 0 for none, as a
34
+ // caller's own scopes take. Two weeks unless given.
35
+ minimumReleaseAge?: number
36
+ // Whether the package is flagged as few download it (`popularity`); false
37
+ // for none, as a caller's own scopes take, its downloads then not asked.
38
+ // True unless given.
39
+ popularity?: boolean
40
+ // Where what is read is kept, by type (risk/npm/registry.js and assess.js
41
+ // name each): `npm/tarballs`, `risk/npm/versions`, `risk/npm/packages`,
42
+ // `risk/npm/old-versions`, `risk/npm/public`, `risk/npm/downloads`,
43
+ // `risk/npm/advisories` and `risk/npm/assessments`. One that reads records
44
+ // of any types at once (`readEach`, each null where none is kept) is read
45
+ // so where risk reads several together.
46
+ cache?: (CacheStore & { readEach?(entries: [type: string, key: string][]): Promise<unknown[]> }) | false
47
+ // Asks for a package's whole document in risk's stead, as the managed
48
+ // server does within its bounds: null where npm has none for this asker.
49
+ // `authorized` whether it may be asked with the caller's npm token: the
50
+ // package assessed, not its dependencies.
51
+ packument?: (name: string, options: { authorized: boolean; signal: AbortSignal | undefined }) => Promise<Record<string, unknown> | null>
52
+ // Reads a version's tarball, gzipped, in place of upstream's getTarball, as
53
+ // the managed server does within its bounds: `version` the one assessed or
54
+ // `oldVersion`, that one only where its files could change what is found.
55
+ // What it throws is thrown as it is.
56
+ tarball?: (name: string, version: string, options: { signal: AbortSignal | undefined }) => Promise<Uint8Array>
57
+ now?: number
58
+ signal?: AbortSignal
59
+ // Whether npm's downloads are asked for: the package's popularity, and its
60
+ // new dependencies' tiers and ages. True unless given.
61
+ downloads?: boolean
62
+ // Whether its new dependencies' tiers and ages are asked for, a request or
63
+ // two of npm each, where `downloads` is. True unless given.
64
+ dependencies?: boolean
65
+ // Whether npm's advisories of the version are asked for, an advisory of
66
+ // malware naming it a `malware` finding, as its package taken down by npm
67
+ // is. True unless given.
68
+ advisories?: boolean
69
+ // Whether OSV's records of malware in the version are asked for too, where
70
+ // `advisories` are: false for a private package, whose name OSV is never
71
+ // to be sent. True unless given.
72
+ osv?: boolean
73
+ // Whether a package is the caller's own, as its own scopes take: a new
74
+ // dependency on one is told no tier nor age, nor is npm asked of it. None
75
+ // unless given.
76
+ owned?: (name: string) => boolean | Promise<boolean>
77
+ }
78
+
79
+ // What calls for a look, by `id`, with what it found beside.
80
+ export interface Finding {
81
+ id: string
82
+ severity: Severity
83
+ [found: string]: unknown
84
+ }
85
+
86
+ // A stamp on a file, as a list of files tags it.
87
+ export interface Stamp {
88
+ name: string
89
+ severity: Severity
90
+ }
91
+
92
+ export interface FileRisk {
93
+ stamps: Stamp[]
94
+ readability: Record<string, unknown>
95
+ runsCode: Record<string, number[]> | null
96
+ exfiltration: Record<string, number[]> | null
97
+ hosts: Record<string, number> | null
98
+ }
99
+
100
+ export interface DependencyRisk {
101
+ key: string
102
+ name: string
103
+ kind: 'peer' | 'optional' | null
104
+ range?: string
105
+ from?: string
106
+ to?: string
107
+ opens: string | null
108
+ flags: { check: string; label: string; tooltip: string }[]
109
+ tier?: number | null
110
+ level?: string
111
+ // Its last whole month's downloads, by what its tier goes by: none where
112
+ // npm counts none of them.
113
+ month?: { total: number; from: string }
114
+ // Whether npm counts none of its downloads, as of a package published
115
+ // the day before; unpopular all the same.
116
+ uncounted?: true
117
+ created?: string
118
+ gap?: number
119
+ // For one nothing imports, the version its spec installs (registry.js
120
+ // npmPublicFacts): its package's latest beside it, when it was published,
121
+ // whether it has install scripts, and whether that was since the version
122
+ // compared with (`new`).
123
+ installs?: { version: string; latest: string | null; published: string | null; scripts: boolean; new: boolean }
124
+ // Where its package was looked up and npm has none of it for anyone.
125
+ missing?: true
126
+ }
127
+
128
+ // A version's risk as one number (risk/npm/score.js): what its findings call
129
+ // for (`raised`, each it counts), each by its severity, malware all of it,
130
+ // lowered by its exposure (`lowered`): how long it has been out (`days`)
131
+ // among how many download its package (`tier`, its rank, null past the last
132
+ // or where unknown), `by` the share taken off. Its `level` is named as the
133
+ // findings' severities are: from 75 critical, 50 high, 25 moderate, else low.
134
+ export interface RiskScore {
135
+ score: number
136
+ level: 'critical' | 'high' | 'moderate' | 'low'
137
+ raised: { id: string; severity: Severity }[]
138
+ lowered?: { days: number; tier: number | null; by: number }
139
+ }
140
+
141
+ export interface NpmAssessment {
142
+ name: string
143
+ version: string
144
+ // The version compared with, as given or as risk picked it; null for none.
145
+ oldVersion: string | null
146
+ published: string | null
147
+ // A version npm removed, as it removes every one it finds malware in, told
148
+ // of by its `malware` finding alone, with no files nor dependencies.
149
+ removed?: true
150
+ findings: Finding[]
151
+ files: Record<string, FileRisk>
152
+ // Against `oldVersion`, each it holds too, flagged against none (`held`).
153
+ dependencies: { added: DependencyRisk[]; changed: DependencyRisk[]; held?: DependencyRisk[] }
154
+ score: RiskScore
155
+ }
156
+
157
+ export function assessNpm(name: string, version: string, options?: AssessNpmOptions): Promise<NpmAssessment>
158
+
159
+ // Readies, at once, the downloads assessNpm asks of each of these packages
160
+ // that can be, kept in `cache` as it keeps them, so that their assessments
161
+ // ask npm none: those kept within a week, and unscoped ones, asked of npm 128
162
+ // at a time. `ready` is told each once its downloads are kept; the rest (scoped
163
+ // ones, which npm answers only one by one) are left for npmReadyDownload.
164
+ // npm asking to be asked later is thrown as an NpmRiskError
165
+ // (`upstream-busy`), those readied by then told.
166
+ // A cache that reads and writes many at once too (`readMany`, `writeMany`)
167
+ // is read once and written once a lookup.
168
+ export function npmReadyDownloads(names: string[], ready: (name: string) => void, options: { cache: CacheStore & {
169
+ readMany?(type: string, keys: string[]): Promise<Map<string, unknown>>
170
+ writeMany?(type: string, entries: [key: string, value: unknown][]): Promise<void>
171
+ }; signal?: AbortSignal; now?: number }): Promise<void>
172
+
173
+ // Readies the downloads assessNpm asks of a package, asked of npm on their
174
+ // own where they aren't kept; npm asking to be asked later thrown as an
175
+ // NpmRiskError (`upstream-busy`).
176
+ export function npmReadyDownload(name: string, options: { cache: CacheStore; signal?: AbortSignal; now?: number }): Promise<void>
177
+
178
+ // Whether an advisory is of malware rather than a vulnerability: OSV's of a
179
+ // malicious package (`MAL-…`), or one such a record names (its `aliases`),
180
+ // CWE-506, embedded malicious code, whatever its title, or a title naming
181
+ // malware.
182
+ export function npmMalwareAdvisory(advisory: { id?: string | undefined; aliases?: readonly string[] | undefined; cwe?: readonly string[] | undefined; title?: string | undefined }): boolean
183
+
184
+ export function npmFileRisk(path: string, text: string | null, bytes?: number, script?: boolean): unknown
185
+
186
+ // GitHub Actions (actions/), a workflow GitHub can't run thrown as
187
+ // @preventive/lockfile's YamlError. A finding of a workflow's, its `line` from 1; of a reference's own code,
188
+ // with the `file` it is in.
189
+ export interface ActionsFinding extends Finding {
190
+ line?: number | null
191
+ file?: string
192
+ }
193
+
194
+ // What a `uses:` names: an action's or a reusable workflow's, by its `key`,
195
+ // or the repository's own.
196
+ export type ActionUses = { kind: 'action' | 'workflow'; uses: string; repo: string; path: string; ref: string; pin: 'sha' | 'short-sha' | 'ref' | 'expression'; comment: string | null; key?: string; line?: number | null }
197
+ export type Uses = ActionUses | { kind: 'local'; uses: string; path: string; self?: true; job?: true; line?: number | null }
198
+
199
+ export interface ScanOptions {
200
+ // Whose repository it is, as an unpinned `uses:` of its owner's is yellow.
201
+ owner?: string | null
202
+ // Its lockfiles' paths, the packages each locks, as npx runs those, by
203
+ // name and by `name@version`, and those only devDependencies need.
204
+ lockfiles?: { path: string; names: ReadonlySet<string>; dev?: ReadonlySet<string> }[]
205
+ // Its own actions' metadata by their directories, and its workflows by
206
+ // their paths: for where its actions install its packages, else each
207
+ // counts as installing them anywhere; and for the inputs its actions and
208
+ // reusable workflows write into their scripts, else each of a reusable
209
+ // workflow's counts as one.
210
+ actions?: ReadonlyMap<string, string> | null
211
+ }
212
+
213
+ export type WorkflowScan = { uses: Uses[]; findings: ActionsFinding[] } | { error: 'workflow-unreadable'; detail: string }
214
+
215
+ export function scanWorkflow(kind: 'workflow' | 'action', text: string, options?: ScanOptions): WorkflowScan
216
+ // The actions of the repository's own a workflow or an action uses, by
217
+ // their directories, and a Docker action's Dockerfile, from its own.
218
+ export function ownFilesOf(kind: 'workflow' | 'action', text: string): { actions: string[]; dockerfile: string | null }
219
+ // A Dockerfile's findings, each on its line.
220
+ export function dockerfileFindings(text: string): ActionsFinding[]
221
+ // A reference by its key, as a scan names it; null for none.
222
+ export function usesOfKey(key: string): ActionUses | null
223
+
224
+ // GitHub as the asker can read it; null for none.
225
+ export interface ActionsSource {
226
+ // Its name now, where it moved, and the repository it forks.
227
+ repo(name: string): Promise<{ fullName: string; defaultBranch: string; fork?: string | null; archived?: boolean; private?: boolean } | null>
228
+ // Every tag's commit; null where it has too many to list.
229
+ tags(name: string): Promise<{ tag: string; oid: string }[] | null>
230
+ tag(name: string, tag: string): Promise<string | null>
231
+ branch(name: string, branch: string): Promise<string | null>
232
+ // Its branches, and whether those are all.
233
+ branches(name: string): Promise<{ branches: { branch: string; oid: string }[]; complete: boolean }>
234
+ // Whether `sha` is `base` or in its history; null for no such commit. Of two
235
+ // commits, it holds for good.
236
+ contains(name: string, base: string, sha: string): Promise<boolean | null>
237
+ file(name: string, path: string, commit: string): Promise<string | null>
238
+ }
239
+
240
+ // A reference resolved: its repository as GitHub has it now, its ref's
241
+ // commit, what the ref is, the tags on the commit, and the keys of the
242
+ // references its code makes; null for none.
243
+ export interface ResolvedAction extends Pick<ActionUses, 'kind' | 'repo' | 'path' | 'ref'> {
244
+ key: string
245
+ repository: { fullName: string; private: boolean } | null
246
+ commit: string | null
247
+ at: 'sha' | 'tag' | 'branch' | null
248
+ tags: string[]
249
+ findings: ActionsFinding[]
250
+ uses: string[]
251
+ }
252
+
253
+ export function resolveAction(uses: ActionUses, source: ActionsSource, options?: { owner?: string | null }): Promise<ResolvedAction>
254
+ export function keptSource(source: ActionsSource): ActionsSource
package/risk/index.js ADDED
@@ -0,0 +1,11 @@
1
+ // What calls for a look at a package version, or a repository's GitHub
2
+ // Actions, for a reviewer: risk's heuristics, apart from any view of them.
3
+ // See README.md.
4
+ export { assessNpm } from './npm/assess.js'
5
+ export { npmMalwareAdvisory } from './npm/malware.js'
6
+ export { DEFAULT_MINIMUM_RELEASE_AGE } from './npm/release.js'
7
+ export { NpmRiskError, npmReadyDownload, npmReadyDownloads } from './npm/registry.js'
8
+ export { SEVERITIES, npmFileRisk } from './files/stamps.js'
9
+ export { ownFilesOf, scanWorkflow } from './actions/findings.js'
10
+ export { dockerfileFindings, keptSource, resolveAction } from './actions/resolve.js'
11
+ export { usesOfKey } from './actions/uses.js'