@m2-ai/lattice-cli 0.0.0-stage → 0.8.7

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 (743) hide show
  1. package/DISCLOSURE +1 -0
  2. package/README.md +118 -2
  3. package/bundles/expo-mobile/.agents/skills/lattice-feature-handoff/SKILL.md +31 -0
  4. package/bundles/expo-mobile/.agents/skills/lattice-release-manager/SKILL.md +29 -0
  5. package/bundles/expo-mobile/.agents/skills/lattice-verify/SKILL.md +27 -0
  6. package/bundles/expo-mobile/.claude/agents/lattice-feature-agent.md +10 -0
  7. package/bundles/expo-mobile/.claude/agents/lattice-release-manager.md +9 -0
  8. package/bundles/expo-mobile/.claude/hooks/README.md +9 -0
  9. package/bundles/expo-mobile/.claude/hooks/pre-commit-verify.mjs +10 -0
  10. package/bundles/expo-mobile/.claude/hooks/pre-tool-use.mjs +51 -0
  11. package/bundles/expo-mobile/.claude/rules/lattice-core.md +20 -0
  12. package/bundles/expo-mobile/.claude/settings.json +38 -0
  13. package/bundles/expo-mobile/.claude/skills/lattice-verify/SKILL.md +27 -0
  14. package/bundles/expo-mobile/.codex/agents/lattice-feature-agent.toml +9 -0
  15. package/bundles/expo-mobile/.codex/agents/lattice-release-manager.toml +9 -0
  16. package/bundles/expo-mobile/.cursor/mcp.json +34 -0
  17. package/bundles/expo-mobile/.cursor/rules/lattice-core.mdc +35 -0
  18. package/bundles/expo-mobile/.cursor/rules.md +94 -0
  19. package/bundles/expo-mobile/.env.example +19 -0
  20. package/bundles/expo-mobile/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  21. package/bundles/expo-mobile/.github/agents/lattice-feature-agent.agent.md +7 -0
  22. package/bundles/expo-mobile/.github/agents/lattice-release-manager.agent.md +7 -0
  23. package/bundles/expo-mobile/.github/copilot-instructions.md +37 -0
  24. package/bundles/expo-mobile/.github/instructions/lattice-core.instructions.md +11 -0
  25. package/bundles/expo-mobile/.github/workflows/ci.yml +26 -0
  26. package/bundles/expo-mobile/.gitmessage +24 -0
  27. package/bundles/expo-mobile/.lattice/config.json +9 -0
  28. package/bundles/expo-mobile/.lattice/dependency-security.json +91 -0
  29. package/bundles/expo-mobile/.lattice/prompts/01-feature.md +36 -0
  30. package/bundles/expo-mobile/.lattice/prompts/02-bugfix.md +32 -0
  31. package/bundles/expo-mobile/.lattice/prompts/03-refactor.md +30 -0
  32. package/bundles/expo-mobile/.lattice/prompts/04-test.md +31 -0
  33. package/bundles/expo-mobile/.lattice/prompts/05-polish.md +38 -0
  34. package/bundles/expo-mobile/.lattice/prompts/06-security.md +32 -0
  35. package/bundles/expo-mobile/.lattice/release-workflow.json +55 -0
  36. package/bundles/expo-mobile/.lattice/schemas/feature-handoff.schema.json +264 -0
  37. package/bundles/expo-mobile/.lattice/templates/feature-handoff.json +52 -0
  38. package/bundles/expo-mobile/.mcp.json +35 -0
  39. package/bundles/expo-mobile/AGENTS.md +58 -0
  40. package/bundles/expo-mobile/CLAUDE.md +75 -0
  41. package/bundles/expo-mobile/PROJECT_GUIDE.md +299 -0
  42. package/bundles/expo-mobile/README.md +39 -0
  43. package/bundles/expo-mobile/__tests__/home.test.tsx +22 -0
  44. package/bundles/expo-mobile/app/_layout.tsx +20 -0
  45. package/bundles/expo-mobile/app/index.tsx +72 -0
  46. package/bundles/expo-mobile/app.json +33 -0
  47. package/bundles/expo-mobile/components/ui/Button.tsx +75 -0
  48. package/bundles/expo-mobile/components/ui/Input.tsx +50 -0
  49. package/bundles/expo-mobile/components/ui/index.ts +2 -0
  50. package/bundles/expo-mobile/docs/LATTICE_RELEASE_WORKFLOW.md +53 -0
  51. package/bundles/expo-mobile/docs/rules/codex.md +62 -0
  52. package/bundles/expo-mobile/eslint.config.js +7 -0
  53. package/bundles/expo-mobile/lib/supabase/client.ts +22 -0
  54. package/bundles/expo-mobile/lib/supabase/realtime.ts +10 -0
  55. package/bundles/expo-mobile/lib/supabase/storage.ts +6 -0
  56. package/bundles/expo-mobile/package.json +65 -0
  57. package/bundles/expo-mobile/plugins/with-lattice-commons-io.cjs +42 -0
  58. package/bundles/expo-mobile/prompts/01-feature-implementation.md +48 -0
  59. package/bundles/expo-mobile/prompts/02-bug-fix.md +43 -0
  60. package/bundles/expo-mobile/prompts/03-safe-refactor.md +38 -0
  61. package/bundles/expo-mobile/prompts/04-test-first.md +41 -0
  62. package/bundles/expo-mobile/prompts/05-ui-polish.md +44 -0
  63. package/bundles/expo-mobile/prompts/06-security-review.md +39 -0
  64. package/bundles/expo-mobile/prompts/quality.md +143 -0
  65. package/bundles/expo-mobile/supabase/migrations/001_initial_schema.sql +66 -0
  66. package/bundles/expo-mobile/tsconfig.json +21 -0
  67. package/bundles/expo-mobile/types/database.types.ts +76 -0
  68. package/bundles/expo-mobile/vendor/m2ai-braces-3.0.3-m2ai.1.tar +0 -0
  69. package/bundles/expo-supabase/.agents/skills/lattice-feature-handoff/SKILL.md +31 -0
  70. package/bundles/expo-supabase/.agents/skills/lattice-release-manager/SKILL.md +29 -0
  71. package/bundles/expo-supabase/.agents/skills/lattice-verify/SKILL.md +27 -0
  72. package/bundles/expo-supabase/.claude/agents/lattice-feature-agent.md +10 -0
  73. package/bundles/expo-supabase/.claude/agents/lattice-release-manager.md +9 -0
  74. package/bundles/expo-supabase/.claude/hooks/README.md +9 -0
  75. package/bundles/expo-supabase/.claude/hooks/pre-commit-verify.mjs +10 -0
  76. package/bundles/expo-supabase/.claude/hooks/pre-tool-use.mjs +51 -0
  77. package/bundles/expo-supabase/.claude/rules/lattice-core.md +20 -0
  78. package/bundles/expo-supabase/.claude/settings.json +38 -0
  79. package/bundles/expo-supabase/.claude/skills/lattice-verify/SKILL.md +27 -0
  80. package/bundles/expo-supabase/.codex/agents/lattice-feature-agent.toml +9 -0
  81. package/bundles/expo-supabase/.codex/agents/lattice-release-manager.toml +9 -0
  82. package/bundles/expo-supabase/.cursor/mcp.json +34 -0
  83. package/bundles/expo-supabase/.cursor/rules/lattice-core.mdc +35 -0
  84. package/bundles/expo-supabase/.cursor/rules.md +94 -0
  85. package/bundles/expo-supabase/.env.example +12 -0
  86. package/bundles/expo-supabase/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  87. package/bundles/expo-supabase/.github/agents/lattice-feature-agent.agent.md +7 -0
  88. package/bundles/expo-supabase/.github/agents/lattice-release-manager.agent.md +7 -0
  89. package/bundles/expo-supabase/.github/copilot-instructions.md +37 -0
  90. package/bundles/expo-supabase/.github/instructions/lattice-core.instructions.md +11 -0
  91. package/bundles/expo-supabase/.github/workflows/ci.yml +26 -0
  92. package/bundles/expo-supabase/.gitmessage +24 -0
  93. package/bundles/expo-supabase/.lattice/config.json +9 -0
  94. package/bundles/expo-supabase/.lattice/dependency-security.json +91 -0
  95. package/bundles/expo-supabase/.lattice/prompts/01-feature.md +36 -0
  96. package/bundles/expo-supabase/.lattice/prompts/02-bugfix.md +32 -0
  97. package/bundles/expo-supabase/.lattice/prompts/03-refactor.md +30 -0
  98. package/bundles/expo-supabase/.lattice/prompts/04-test.md +31 -0
  99. package/bundles/expo-supabase/.lattice/prompts/05-polish.md +38 -0
  100. package/bundles/expo-supabase/.lattice/prompts/06-security.md +32 -0
  101. package/bundles/expo-supabase/.lattice/release-workflow.json +55 -0
  102. package/bundles/expo-supabase/.lattice/schemas/feature-handoff.schema.json +264 -0
  103. package/bundles/expo-supabase/.lattice/templates/feature-handoff.json +52 -0
  104. package/bundles/expo-supabase/.mcp.json +35 -0
  105. package/bundles/expo-supabase/AGENTS.md +58 -0
  106. package/bundles/expo-supabase/CLAUDE.md +75 -0
  107. package/bundles/expo-supabase/PROJECT_GUIDE.md +201 -0
  108. package/bundles/expo-supabase/README.md +38 -0
  109. package/bundles/expo-supabase/__tests__/home.test.tsx +22 -0
  110. package/bundles/expo-supabase/app/_layout.tsx +14 -0
  111. package/bundles/expo-supabase/app/index.tsx +72 -0
  112. package/bundles/expo-supabase/app.json +33 -0
  113. package/bundles/expo-supabase/components/ui/Button.tsx +75 -0
  114. package/bundles/expo-supabase/components/ui/Input.tsx +50 -0
  115. package/bundles/expo-supabase/components/ui/index.ts +2 -0
  116. package/bundles/expo-supabase/docs/LATTICE_RELEASE_WORKFLOW.md +53 -0
  117. package/bundles/expo-supabase/docs/rules/codex.md +62 -0
  118. package/bundles/expo-supabase/eslint.config.js +7 -0
  119. package/bundles/expo-supabase/lib/supabase/client.ts +22 -0
  120. package/bundles/expo-supabase/lib/supabase/realtime.ts +10 -0
  121. package/bundles/expo-supabase/lib/supabase/storage.ts +6 -0
  122. package/bundles/expo-supabase/package.json +60 -0
  123. package/bundles/expo-supabase/plugins/with-lattice-commons-io.cjs +42 -0
  124. package/bundles/expo-supabase/prompts/01-feature-implementation.md +8 -0
  125. package/bundles/expo-supabase/prompts/02-bug-fix.md +9 -0
  126. package/bundles/expo-supabase/prompts/03-safe-refactor.md +8 -0
  127. package/bundles/expo-supabase/prompts/04-test-first.md +7 -0
  128. package/bundles/expo-supabase/prompts/05-ui-polish.md +7 -0
  129. package/bundles/expo-supabase/prompts/06-security-review.md +7 -0
  130. package/bundles/expo-supabase/prompts/quality.md +143 -0
  131. package/bundles/expo-supabase/supabase/migrations/001_initial_schema.sql +66 -0
  132. package/bundles/expo-supabase/tsconfig.json +21 -0
  133. package/bundles/expo-supabase/types/database.types.ts +76 -0
  134. package/bundles/expo-supabase/vendor/m2ai-braces-3.0.3-m2ai.1.tar +0 -0
  135. package/bundles/nextjs-landing/.agents/skills/lattice-feature-handoff/SKILL.md +31 -0
  136. package/bundles/nextjs-landing/.agents/skills/lattice-release-manager/SKILL.md +29 -0
  137. package/bundles/nextjs-landing/.agents/skills/lattice-verify/SKILL.md +27 -0
  138. package/bundles/nextjs-landing/.claude/agents/lattice-feature-agent.md +10 -0
  139. package/bundles/nextjs-landing/.claude/agents/lattice-release-manager.md +9 -0
  140. package/bundles/nextjs-landing/.claude/hooks/README.md +9 -0
  141. package/bundles/nextjs-landing/.claude/hooks/pre-commit-verify.mjs +10 -0
  142. package/bundles/nextjs-landing/.claude/hooks/pre-tool-use.mjs +51 -0
  143. package/bundles/nextjs-landing/.claude/rules/lattice-core.md +20 -0
  144. package/bundles/nextjs-landing/.claude/settings.json +38 -0
  145. package/bundles/nextjs-landing/.claude/skills/lattice-verify/SKILL.md +27 -0
  146. package/bundles/nextjs-landing/.codex/agents/lattice-feature-agent.toml +9 -0
  147. package/bundles/nextjs-landing/.codex/agents/lattice-release-manager.toml +9 -0
  148. package/bundles/nextjs-landing/.cursor/mcp.json +34 -0
  149. package/bundles/nextjs-landing/.cursor/rules/lattice-core.mdc +35 -0
  150. package/bundles/nextjs-landing/.cursor/rules.md +95 -0
  151. package/bundles/nextjs-landing/.env.example +24 -0
  152. package/bundles/nextjs-landing/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  153. package/bundles/nextjs-landing/.github/agents/lattice-feature-agent.agent.md +7 -0
  154. package/bundles/nextjs-landing/.github/agents/lattice-release-manager.agent.md +7 -0
  155. package/bundles/nextjs-landing/.github/copilot-instructions.md +37 -0
  156. package/bundles/nextjs-landing/.github/instructions/lattice-core.instructions.md +11 -0
  157. package/bundles/nextjs-landing/.github/workflows/ci.yml +26 -0
  158. package/bundles/nextjs-landing/.gitmessage +24 -0
  159. package/bundles/nextjs-landing/.lattice/config.json +9 -0
  160. package/bundles/nextjs-landing/.lattice/dependency-security.json +46 -0
  161. package/bundles/nextjs-landing/.lattice/prompts/01-feature.md +36 -0
  162. package/bundles/nextjs-landing/.lattice/prompts/02-bugfix.md +32 -0
  163. package/bundles/nextjs-landing/.lattice/prompts/03-refactor.md +30 -0
  164. package/bundles/nextjs-landing/.lattice/prompts/04-test.md +31 -0
  165. package/bundles/nextjs-landing/.lattice/prompts/05-polish.md +38 -0
  166. package/bundles/nextjs-landing/.lattice/prompts/06-security.md +32 -0
  167. package/bundles/nextjs-landing/.lattice/release-workflow.json +55 -0
  168. package/bundles/nextjs-landing/.lattice/schemas/feature-handoff.schema.json +264 -0
  169. package/bundles/nextjs-landing/.lattice/templates/feature-handoff.json +52 -0
  170. package/bundles/nextjs-landing/.mcp.json +35 -0
  171. package/bundles/nextjs-landing/AGENTS.md +58 -0
  172. package/bundles/nextjs-landing/CLAUDE.md +76 -0
  173. package/bundles/nextjs-landing/PROJECT_GUIDE.md +216 -0
  174. package/bundles/nextjs-landing/README.md +39 -0
  175. package/bundles/nextjs-landing/app/api/example/[id]/route.ts +34 -0
  176. package/bundles/nextjs-landing/app/api/example/route.ts +24 -0
  177. package/bundles/nextjs-landing/app/api/health/route.ts +8 -0
  178. package/bundles/nextjs-landing/app/global-error.tsx +17 -0
  179. package/bundles/nextjs-landing/app/globals.css +55 -0
  180. package/bundles/nextjs-landing/app/layout.tsx +22 -0
  181. package/bundles/nextjs-landing/app/page.tsx +42 -0
  182. package/bundles/nextjs-landing/components/ui/button.tsx +39 -0
  183. package/bundles/nextjs-landing/components/ui/card.tsx +53 -0
  184. package/bundles/nextjs-landing/components/ui/index.ts +4 -0
  185. package/bundles/nextjs-landing/components/ui/input.tsx +23 -0
  186. package/bundles/nextjs-landing/components/ui/spinner.tsx +22 -0
  187. package/bundles/nextjs-landing/docs/LATTICE_RELEASE_WORKFLOW.md +53 -0
  188. package/bundles/nextjs-landing/docs/rules/codex.md +62 -0
  189. package/bundles/nextjs-landing/eslint.config.mjs +16 -0
  190. package/bundles/nextjs-landing/instrumentation-client.ts +30 -0
  191. package/bundles/nextjs-landing/instrumentation.ts +12 -0
  192. package/bundles/nextjs-landing/jest.config.mjs +16 -0
  193. package/bundles/nextjs-landing/jest.setup.js +1 -0
  194. package/bundles/nextjs-landing/lib/utils.ts +6 -0
  195. package/bundles/nextjs-landing/next-env.d.ts +5 -0
  196. package/bundles/nextjs-landing/next.config.ts +15 -0
  197. package/bundles/nextjs-landing/package.json +48 -0
  198. package/bundles/nextjs-landing/postcss.config.mjs +7 -0
  199. package/bundles/nextjs-landing/prompts/01-feature-implementation.md +9 -0
  200. package/bundles/nextjs-landing/prompts/02-bug-fix.md +10 -0
  201. package/bundles/nextjs-landing/prompts/03-safe-refactor.md +8 -0
  202. package/bundles/nextjs-landing/prompts/04-test-first.md +8 -0
  203. package/bundles/nextjs-landing/prompts/05-ui-polish.md +8 -0
  204. package/bundles/nextjs-landing/prompts/06-security-review.md +7 -0
  205. package/bundles/nextjs-landing/prompts/quality.md +269 -0
  206. package/bundles/nextjs-landing/sentry.edge.config.ts +15 -0
  207. package/bundles/nextjs-landing/sentry.server.config.ts +15 -0
  208. package/bundles/nextjs-landing/tsconfig.json +42 -0
  209. package/bundles/nextjs-landing/vendor/m2ai-braces-3.0.3-m2ai.1.tar +0 -0
  210. package/bundles/nextjs-saas/.agents/skills/lattice-feature-handoff/SKILL.md +31 -0
  211. package/bundles/nextjs-saas/.agents/skills/lattice-release-manager/SKILL.md +29 -0
  212. package/bundles/nextjs-saas/.agents/skills/lattice-verify/SKILL.md +27 -0
  213. package/bundles/nextjs-saas/.claude/agents/lattice-feature-agent.md +10 -0
  214. package/bundles/nextjs-saas/.claude/agents/lattice-release-manager.md +9 -0
  215. package/bundles/nextjs-saas/.claude/hooks/README.md +9 -0
  216. package/bundles/nextjs-saas/.claude/hooks/pre-commit-verify.mjs +10 -0
  217. package/bundles/nextjs-saas/.claude/hooks/pre-tool-use.mjs +51 -0
  218. package/bundles/nextjs-saas/.claude/rules/lattice-core.md +20 -0
  219. package/bundles/nextjs-saas/.claude/settings.json +38 -0
  220. package/bundles/nextjs-saas/.claude/skills/lattice-verify/SKILL.md +27 -0
  221. package/bundles/nextjs-saas/.codex/agents/lattice-feature-agent.toml +9 -0
  222. package/bundles/nextjs-saas/.codex/agents/lattice-release-manager.toml +9 -0
  223. package/bundles/nextjs-saas/.cursor/mcp.json +34 -0
  224. package/bundles/nextjs-saas/.cursor/rules/lattice-core.mdc +35 -0
  225. package/bundles/nextjs-saas/.cursor/rules.md +95 -0
  226. package/bundles/nextjs-saas/.env.example +48 -0
  227. package/bundles/nextjs-saas/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  228. package/bundles/nextjs-saas/.github/agents/lattice-feature-agent.agent.md +7 -0
  229. package/bundles/nextjs-saas/.github/agents/lattice-release-manager.agent.md +7 -0
  230. package/bundles/nextjs-saas/.github/copilot-instructions.md +37 -0
  231. package/bundles/nextjs-saas/.github/instructions/lattice-core.instructions.md +11 -0
  232. package/bundles/nextjs-saas/.github/workflows/ci.yml +26 -0
  233. package/bundles/nextjs-saas/.gitmessage +24 -0
  234. package/bundles/nextjs-saas/.lattice/config.json +9 -0
  235. package/bundles/nextjs-saas/.lattice/dependency-security.json +46 -0
  236. package/bundles/nextjs-saas/.lattice/prompts/01-feature.md +36 -0
  237. package/bundles/nextjs-saas/.lattice/prompts/02-bugfix.md +32 -0
  238. package/bundles/nextjs-saas/.lattice/prompts/03-refactor.md +30 -0
  239. package/bundles/nextjs-saas/.lattice/prompts/04-test.md +31 -0
  240. package/bundles/nextjs-saas/.lattice/prompts/05-polish.md +38 -0
  241. package/bundles/nextjs-saas/.lattice/prompts/06-security.md +32 -0
  242. package/bundles/nextjs-saas/.lattice/prompts/quality.md +269 -0
  243. package/bundles/nextjs-saas/.lattice/release-workflow.json +55 -0
  244. package/bundles/nextjs-saas/.lattice/schemas/feature-handoff.schema.json +264 -0
  245. package/bundles/nextjs-saas/.lattice/templates/feature-handoff.json +52 -0
  246. package/bundles/nextjs-saas/.mcp.json +35 -0
  247. package/bundles/nextjs-saas/AGENTS.md +58 -0
  248. package/bundles/nextjs-saas/CLAUDE.md +76 -0
  249. package/bundles/nextjs-saas/PROJECT_GUIDE.md +279 -0
  250. package/bundles/nextjs-saas/README.md +42 -0
  251. package/bundles/nextjs-saas/app/(auth)/forgot-password/page.tsx +101 -0
  252. package/bundles/nextjs-saas/app/(auth)/layout.tsx +7 -0
  253. package/bundles/nextjs-saas/app/(auth)/login/page.tsx +108 -0
  254. package/bundles/nextjs-saas/app/(auth)/reset-password/page.tsx +135 -0
  255. package/bundles/nextjs-saas/app/(auth)/signup/page.tsx +144 -0
  256. package/bundles/nextjs-saas/app/(dashboard)/dashboard/page.tsx +80 -0
  257. package/bundles/nextjs-saas/app/(dashboard)/layout.tsx +49 -0
  258. package/bundles/nextjs-saas/app/api/billing/checkout/route.ts +47 -0
  259. package/bundles/nextjs-saas/app/api/billing/portal/route.ts +29 -0
  260. package/bundles/nextjs-saas/app/api/example/[id]/route.ts +93 -0
  261. package/bundles/nextjs-saas/app/api/example/route.ts +61 -0
  262. package/bundles/nextjs-saas/app/api/health/route.ts +8 -0
  263. package/bundles/nextjs-saas/app/api/stripe/webhook/route.ts +57 -0
  264. package/bundles/nextjs-saas/app/api/user/route.ts +49 -0
  265. package/bundles/nextjs-saas/app/auth/callback/route.ts +16 -0
  266. package/bundles/nextjs-saas/app/auth/signout/route.ts +10 -0
  267. package/bundles/nextjs-saas/app/global-error.tsx +17 -0
  268. package/bundles/nextjs-saas/app/globals.css +55 -0
  269. package/bundles/nextjs-saas/app/layout.tsx +22 -0
  270. package/bundles/nextjs-saas/app/page.tsx +84 -0
  271. package/bundles/nextjs-saas/components/ui/button.tsx +39 -0
  272. package/bundles/nextjs-saas/components/ui/card.tsx +53 -0
  273. package/bundles/nextjs-saas/components/ui/index.ts +4 -0
  274. package/bundles/nextjs-saas/components/ui/input.tsx +23 -0
  275. package/bundles/nextjs-saas/components/ui/spinner.tsx +22 -0
  276. package/bundles/nextjs-saas/docs/LATTICE_RELEASE_WORKFLOW.md +53 -0
  277. package/bundles/nextjs-saas/docs/rules/codex.md +62 -0
  278. package/bundles/nextjs-saas/eslint.config.mjs +16 -0
  279. package/bundles/nextjs-saas/instrumentation-client.ts +30 -0
  280. package/bundles/nextjs-saas/instrumentation.ts +12 -0
  281. package/bundles/nextjs-saas/jest.config.mjs +16 -0
  282. package/bundles/nextjs-saas/jest.setup.js +1 -0
  283. package/bundles/nextjs-saas/lib/auth/password-recovery.ts +94 -0
  284. package/bundles/nextjs-saas/lib/stripe.ts +10 -0
  285. package/bundles/nextjs-saas/lib/supabase/admin.ts +14 -0
  286. package/bundles/nextjs-saas/lib/supabase/client.ts +8 -0
  287. package/bundles/nextjs-saas/lib/supabase/server.ts +27 -0
  288. package/bundles/nextjs-saas/lib/utils.ts +6 -0
  289. package/bundles/nextjs-saas/next-env.d.ts +5 -0
  290. package/bundles/nextjs-saas/next.config.ts +15 -0
  291. package/bundles/nextjs-saas/package.json +50 -0
  292. package/bundles/nextjs-saas/postcss.config.mjs +7 -0
  293. package/bundles/nextjs-saas/prompts/01-feature-implementation.md +92 -0
  294. package/bundles/nextjs-saas/prompts/02-bug-fix.md +78 -0
  295. package/bundles/nextjs-saas/prompts/03-safe-refactor.md +82 -0
  296. package/bundles/nextjs-saas/prompts/04-test-first.md +80 -0
  297. package/bundles/nextjs-saas/prompts/05-ui-polish.md +82 -0
  298. package/bundles/nextjs-saas/prompts/06-security-review.md +74 -0
  299. package/bundles/nextjs-saas/proxy.ts +61 -0
  300. package/bundles/nextjs-saas/sentry.edge.config.ts +15 -0
  301. package/bundles/nextjs-saas/sentry.server.config.ts +15 -0
  302. package/bundles/nextjs-saas/supabase/migrations/001_initial_schema.sql +66 -0
  303. package/bundles/nextjs-saas/supabase/migrations/002_billing.sql +17 -0
  304. package/bundles/nextjs-saas/tsconfig.json +42 -0
  305. package/bundles/nextjs-saas/types/database.types.ts +76 -0
  306. package/bundles/nextjs-saas/vendor/m2ai-braces-3.0.3-m2ai.1.tar +0 -0
  307. package/bundles/nextjs-trpc/.agents/skills/lattice-feature-handoff/SKILL.md +31 -0
  308. package/bundles/nextjs-trpc/.agents/skills/lattice-release-manager/SKILL.md +29 -0
  309. package/bundles/nextjs-trpc/.agents/skills/lattice-verify/SKILL.md +27 -0
  310. package/bundles/nextjs-trpc/.claude/agents/lattice-feature-agent.md +10 -0
  311. package/bundles/nextjs-trpc/.claude/agents/lattice-release-manager.md +9 -0
  312. package/bundles/nextjs-trpc/.claude/hooks/README.md +9 -0
  313. package/bundles/nextjs-trpc/.claude/hooks/pre-commit-verify.mjs +10 -0
  314. package/bundles/nextjs-trpc/.claude/hooks/pre-tool-use.mjs +51 -0
  315. package/bundles/nextjs-trpc/.claude/rules/lattice-core.md +20 -0
  316. package/bundles/nextjs-trpc/.claude/settings.json +38 -0
  317. package/bundles/nextjs-trpc/.claude/skills/lattice-verify/SKILL.md +27 -0
  318. package/bundles/nextjs-trpc/.codex/agents/lattice-feature-agent.toml +9 -0
  319. package/bundles/nextjs-trpc/.codex/agents/lattice-release-manager.toml +9 -0
  320. package/bundles/nextjs-trpc/.cursor/mcp.json +34 -0
  321. package/bundles/nextjs-trpc/.cursor/rules/lattice-core.mdc +35 -0
  322. package/bundles/nextjs-trpc/.cursor/rules.md +95 -0
  323. package/bundles/nextjs-trpc/.env.example +21 -0
  324. package/bundles/nextjs-trpc/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  325. package/bundles/nextjs-trpc/.github/agents/lattice-feature-agent.agent.md +7 -0
  326. package/bundles/nextjs-trpc/.github/agents/lattice-release-manager.agent.md +7 -0
  327. package/bundles/nextjs-trpc/.github/copilot-instructions.md +37 -0
  328. package/bundles/nextjs-trpc/.github/instructions/lattice-core.instructions.md +11 -0
  329. package/bundles/nextjs-trpc/.github/workflows/ci.yml +26 -0
  330. package/bundles/nextjs-trpc/.gitmessage +24 -0
  331. package/bundles/nextjs-trpc/.lattice/config.json +9 -0
  332. package/bundles/nextjs-trpc/.lattice/dependency-security.json +46 -0
  333. package/bundles/nextjs-trpc/.lattice/prompts/01-feature.md +36 -0
  334. package/bundles/nextjs-trpc/.lattice/prompts/02-bugfix.md +32 -0
  335. package/bundles/nextjs-trpc/.lattice/prompts/03-refactor.md +30 -0
  336. package/bundles/nextjs-trpc/.lattice/prompts/04-test.md +31 -0
  337. package/bundles/nextjs-trpc/.lattice/prompts/05-polish.md +38 -0
  338. package/bundles/nextjs-trpc/.lattice/prompts/06-security.md +32 -0
  339. package/bundles/nextjs-trpc/.lattice/release-workflow.json +55 -0
  340. package/bundles/nextjs-trpc/.lattice/schemas/feature-handoff.schema.json +264 -0
  341. package/bundles/nextjs-trpc/.lattice/templates/feature-handoff.json +52 -0
  342. package/bundles/nextjs-trpc/.mcp.json +35 -0
  343. package/bundles/nextjs-trpc/AGENTS.md +58 -0
  344. package/bundles/nextjs-trpc/CLAUDE.md +76 -0
  345. package/bundles/nextjs-trpc/PROJECT_GUIDE.md +187 -0
  346. package/bundles/nextjs-trpc/README.md +49 -0
  347. package/bundles/nextjs-trpc/app/api/trpc/[trpc]/route.ts +12 -0
  348. package/bundles/nextjs-trpc/app/global-error.tsx +17 -0
  349. package/bundles/nextjs-trpc/app/globals.css +55 -0
  350. package/bundles/nextjs-trpc/app/layout.tsx +19 -0
  351. package/bundles/nextjs-trpc/app/page.tsx +16 -0
  352. package/bundles/nextjs-trpc/app/providers.tsx +24 -0
  353. package/bundles/nextjs-trpc/components/ui/button.tsx +39 -0
  354. package/bundles/nextjs-trpc/components/ui/card.tsx +53 -0
  355. package/bundles/nextjs-trpc/components/ui/index.ts +4 -0
  356. package/bundles/nextjs-trpc/components/ui/input.tsx +23 -0
  357. package/bundles/nextjs-trpc/components/ui/spinner.tsx +22 -0
  358. package/bundles/nextjs-trpc/components/user-list.tsx +17 -0
  359. package/bundles/nextjs-trpc/docs/LATTICE_RELEASE_WORKFLOW.md +53 -0
  360. package/bundles/nextjs-trpc/docs/rules/codex.md +62 -0
  361. package/bundles/nextjs-trpc/eslint.config.mjs +16 -0
  362. package/bundles/nextjs-trpc/instrumentation-client.ts +20 -0
  363. package/bundles/nextjs-trpc/instrumentation.ts +12 -0
  364. package/bundles/nextjs-trpc/jest.config.mjs +16 -0
  365. package/bundles/nextjs-trpc/jest.setup.js +1 -0
  366. package/bundles/nextjs-trpc/lib/db.ts +17 -0
  367. package/bundles/nextjs-trpc/lib/trpc/react.tsx +7 -0
  368. package/bundles/nextjs-trpc/lib/utils.ts +6 -0
  369. package/bundles/nextjs-trpc/next-env.d.ts +5 -0
  370. package/bundles/nextjs-trpc/next.config.ts +15 -0
  371. package/bundles/nextjs-trpc/package.json +62 -0
  372. package/bundles/nextjs-trpc/postcss.config.mjs +7 -0
  373. package/bundles/nextjs-trpc/prisma/schema.prisma +41 -0
  374. package/bundles/nextjs-trpc/prisma.config.ts +10 -0
  375. package/bundles/nextjs-trpc/prompts/01-feature-implementation.md +9 -0
  376. package/bundles/nextjs-trpc/prompts/02-bug-fix.md +9 -0
  377. package/bundles/nextjs-trpc/prompts/03-safe-refactor.md +8 -0
  378. package/bundles/nextjs-trpc/prompts/04-test-first.md +8 -0
  379. package/bundles/nextjs-trpc/prompts/05-ui-polish.md +7 -0
  380. package/bundles/nextjs-trpc/prompts/06-security-review.md +7 -0
  381. package/bundles/nextjs-trpc/prompts/quality.md +269 -0
  382. package/bundles/nextjs-trpc/sentry.edge.config.ts +15 -0
  383. package/bundles/nextjs-trpc/sentry.server.config.ts +15 -0
  384. package/bundles/nextjs-trpc/server/routers/_app.test.ts +60 -0
  385. package/bundles/nextjs-trpc/server/routers/_app.ts +25 -0
  386. package/bundles/nextjs-trpc/server/trpc.ts +12 -0
  387. package/bundles/nextjs-trpc/tsconfig.json +42 -0
  388. package/bundles/nextjs-trpc/vendor/m2ai-braces-3.0.3-m2ai.1.tar +0 -0
  389. package/dist/agent-install-session.d.ts +17 -0
  390. package/dist/agent-install-session.d.ts.map +1 -0
  391. package/dist/agent-install-session.js +106 -0
  392. package/dist/agent-install-session.js.map +1 -0
  393. package/dist/agent-install-session.test.d.ts +2 -0
  394. package/dist/agent-install-session.test.d.ts.map +1 -0
  395. package/dist/agent-install-session.test.js +62 -0
  396. package/dist/agent-install-session.test.js.map +1 -0
  397. package/dist/agent-install.d.ts +41 -0
  398. package/dist/agent-install.d.ts.map +1 -0
  399. package/dist/agent-install.js +42 -0
  400. package/dist/agent-install.js.map +1 -0
  401. package/dist/agent-install.test.d.ts +2 -0
  402. package/dist/agent-install.test.d.ts.map +1 -0
  403. package/dist/agent-install.test.js +58 -0
  404. package/dist/agent-install.test.js.map +1 -0
  405. package/dist/apply-merge.test.d.ts +2 -0
  406. package/dist/apply-merge.test.d.ts.map +1 -0
  407. package/dist/apply-merge.test.js +123 -0
  408. package/dist/apply-merge.test.js.map +1 -0
  409. package/dist/bundle-download.d.ts +23 -0
  410. package/dist/bundle-download.d.ts.map +1 -0
  411. package/dist/bundle-download.js +180 -0
  412. package/dist/bundle-download.js.map +1 -0
  413. package/dist/bundle-download.test.d.ts +2 -0
  414. package/dist/bundle-download.test.d.ts.map +1 -0
  415. package/dist/bundle-download.test.js +200 -0
  416. package/dist/bundle-download.test.js.map +1 -0
  417. package/dist/bundle-rollback.test.d.ts +2 -0
  418. package/dist/bundle-rollback.test.d.ts.map +1 -0
  419. package/dist/bundle-rollback.test.js +127 -0
  420. package/dist/bundle-rollback.test.js.map +1 -0
  421. package/dist/bundle-scaffold.test.d.ts +2 -0
  422. package/dist/bundle-scaffold.test.d.ts.map +1 -0
  423. package/dist/bundle-scaffold.test.js +105 -0
  424. package/dist/bundle-scaffold.test.js.map +1 -0
  425. package/dist/commands.d.ts +132 -0
  426. package/dist/commands.d.ts.map +1 -0
  427. package/dist/commands.js +2012 -0
  428. package/dist/commands.js.map +1 -0
  429. package/dist/credential-store.d.ts +27 -0
  430. package/dist/credential-store.d.ts.map +1 -0
  431. package/dist/credential-store.js +48 -0
  432. package/dist/credential-store.js.map +1 -0
  433. package/dist/credential-store.test.d.ts +2 -0
  434. package/dist/credential-store.test.d.ts.map +1 -0
  435. package/dist/credential-store.test.js +80 -0
  436. package/dist/credential-store.test.js.map +1 -0
  437. package/dist/delivered-project-files.d.ts +31 -0
  438. package/dist/delivered-project-files.d.ts.map +1 -0
  439. package/dist/delivered-project-files.js +158 -0
  440. package/dist/delivered-project-files.js.map +1 -0
  441. package/dist/delivered-project-files.test.d.ts +2 -0
  442. package/dist/delivered-project-files.test.d.ts.map +1 -0
  443. package/dist/delivered-project-files.test.js +107 -0
  444. package/dist/delivered-project-files.test.js.map +1 -0
  445. package/dist/doctor-agents.test.d.ts +2 -0
  446. package/dist/doctor-agents.test.d.ts.map +1 -0
  447. package/dist/doctor-agents.test.js +65 -0
  448. package/dist/doctor-agents.test.js.map +1 -0
  449. package/dist/hermes-install.d.ts +102 -0
  450. package/dist/hermes-install.d.ts.map +1 -0
  451. package/dist/hermes-install.js +641 -0
  452. package/dist/hermes-install.js.map +1 -0
  453. package/dist/hermes-install.test.d.ts +2 -0
  454. package/dist/hermes-install.test.d.ts.map +1 -0
  455. package/dist/hermes-install.test.js +866 -0
  456. package/dist/hermes-install.test.js.map +1 -0
  457. package/dist/index.d.ts +115 -0
  458. package/dist/index.d.ts.map +1 -0
  459. package/dist/index.js +5311 -0
  460. package/dist/index.js.map +1 -0
  461. package/dist/openclaw-install.d.ts +67 -0
  462. package/dist/openclaw-install.d.ts.map +1 -0
  463. package/dist/openclaw-install.js +358 -0
  464. package/dist/openclaw-install.js.map +1 -0
  465. package/dist/openclaw-install.test.d.ts +2 -0
  466. package/dist/openclaw-install.test.d.ts.map +1 -0
  467. package/dist/openclaw-install.test.js +296 -0
  468. package/dist/openclaw-install.test.js.map +1 -0
  469. package/dist/password-recovery-bundle.test.d.ts +2 -0
  470. package/dist/password-recovery-bundle.test.d.ts.map +1 -0
  471. package/dist/password-recovery-bundle.test.js +132 -0
  472. package/dist/password-recovery-bundle.test.js.map +1 -0
  473. package/dist/project-bundle-env.d.ts +19 -0
  474. package/dist/project-bundle-env.d.ts.map +1 -0
  475. package/dist/project-bundle-env.js +111 -0
  476. package/dist/project-bundle-env.js.map +1 -0
  477. package/dist/project-bundle-env.test.d.ts +2 -0
  478. package/dist/project-bundle-env.test.d.ts.map +1 -0
  479. package/dist/project-bundle-env.test.js +93 -0
  480. package/dist/project-bundle-env.test.js.map +1 -0
  481. package/dist/project-path-confinement.test.d.ts +2 -0
  482. package/dist/project-path-confinement.test.d.ts.map +1 -0
  483. package/dist/project-path-confinement.test.js +222 -0
  484. package/dist/project-path-confinement.test.js.map +1 -0
  485. package/dist/security/api.d.ts +18 -0
  486. package/dist/security/api.d.ts.map +1 -0
  487. package/dist/security/api.js +238 -0
  488. package/dist/security/api.js.map +1 -0
  489. package/dist/security/api.test.d.ts +2 -0
  490. package/dist/security/api.test.d.ts.map +1 -0
  491. package/dist/security/api.test.js +220 -0
  492. package/dist/security/api.test.js.map +1 -0
  493. package/dist/security/artifact.d.ts +10 -0
  494. package/dist/security/artifact.d.ts.map +1 -0
  495. package/dist/security/artifact.js +283 -0
  496. package/dist/security/artifact.js.map +1 -0
  497. package/dist/security/artifact.test.d.ts +2 -0
  498. package/dist/security/artifact.test.d.ts.map +1 -0
  499. package/dist/security/artifact.test.js +229 -0
  500. package/dist/security/artifact.test.js.map +1 -0
  501. package/dist/security/commands.d.ts +42 -0
  502. package/dist/security/commands.d.ts.map +1 -0
  503. package/dist/security/commands.js +825 -0
  504. package/dist/security/commands.js.map +1 -0
  505. package/dist/security/commands.test.d.ts +2 -0
  506. package/dist/security/commands.test.d.ts.map +1 -0
  507. package/dist/security/commands.test.js +1051 -0
  508. package/dist/security/commands.test.js.map +1 -0
  509. package/dist/security/connection.d.ts +19 -0
  510. package/dist/security/connection.d.ts.map +1 -0
  511. package/dist/security/connection.js +145 -0
  512. package/dist/security/connection.js.map +1 -0
  513. package/dist/security/connection.test.d.ts +2 -0
  514. package/dist/security/connection.test.d.ts.map +1 -0
  515. package/dist/security/connection.test.js +62 -0
  516. package/dist/security/connection.test.js.map +1 -0
  517. package/dist/security/index-routing.test.d.ts +2 -0
  518. package/dist/security/index-routing.test.d.ts.map +1 -0
  519. package/dist/security/index-routing.test.js +169 -0
  520. package/dist/security/index-routing.test.js.map +1 -0
  521. package/dist/security/journal.d.ts +26 -0
  522. package/dist/security/journal.d.ts.map +1 -0
  523. package/dist/security/journal.js +441 -0
  524. package/dist/security/journal.js.map +1 -0
  525. package/dist/security/journal.test.d.ts +2 -0
  526. package/dist/security/journal.test.d.ts.map +1 -0
  527. package/dist/security/journal.test.js +226 -0
  528. package/dist/security/journal.test.js.map +1 -0
  529. package/dist/security/package-verifier.d.ts +15 -0
  530. package/dist/security/package-verifier.d.ts.map +1 -0
  531. package/dist/security/package-verifier.js +65 -0
  532. package/dist/security/package-verifier.js.map +1 -0
  533. package/dist/security/package-verifier.test.d.ts +2 -0
  534. package/dist/security/package-verifier.test.d.ts.map +1 -0
  535. package/dist/security/package-verifier.test.js +57 -0
  536. package/dist/security/package-verifier.test.js.map +1 -0
  537. package/dist/security/runtime.d.ts +43 -0
  538. package/dist/security/runtime.d.ts.map +1 -0
  539. package/dist/security/runtime.js +81 -0
  540. package/dist/security/runtime.js.map +1 -0
  541. package/dist/security/runtime.test.d.ts +2 -0
  542. package/dist/security/runtime.test.d.ts.map +1 -0
  543. package/dist/security/runtime.test.js +88 -0
  544. package/dist/security/runtime.test.js.map +1 -0
  545. package/dist/security/types.d.ts +169 -0
  546. package/dist/security/types.d.ts.map +1 -0
  547. package/dist/security/types.js +3 -0
  548. package/dist/security/types.js.map +1 -0
  549. package/dist/security-runtime/LICENSE +202 -0
  550. package/dist/security-runtime/NOTICE +16 -0
  551. package/dist/security-runtime/artifact-manifest.json +960 -0
  552. package/dist/security-runtime/bin/vsg.mjs +5 -0
  553. package/dist/security-runtime/lattice-catalog.json +176585 -0
  554. package/dist/security-runtime/lattice-runtime.mjs +90 -0
  555. package/dist/security-runtime/licensing/candidates/apache-source-license-evidence.json +478 -0
  556. package/dist/security-runtime/licensing/candidates/betterleaks-generic-api-key.json +76 -0
  557. package/dist/security-runtime/licensing/candidates/betterleaks-jwt.json +74 -0
  558. package/dist/security-runtime/licensing/candidates/blackberry-additional-source-declaration-evidence.json +276 -0
  559. package/dist/security-runtime/licensing/candidates/blackberry-source-declaration-evidence.json +369 -0
  560. package/dist/security-runtime/licensing/candidates/cado-source-declaration-evidence.json +283 -0
  561. package/dist/security-runtime/licensing/candidates/commercial-rule-candidate.json +280 -0
  562. package/dist/security-runtime/licensing/candidates/commercial-runtime-release.json +389 -0
  563. package/dist/security-runtime/licensing/candidates/data-feed-evidence.json +259 -0
  564. package/dist/security-runtime/licensing/candidates/gitlab-sast-rule-identities.json +178 -0
  565. package/dist/security-runtime/licensing/candidates/gitlab-sast-source-evidence.json +1824 -0
  566. package/dist/security-runtime/licensing/candidates/gitlab-sast-source-notice-bundle.json +45 -0
  567. package/dist/security-runtime/licensing/candidates/malpedia-source-license-evidence.json +41 -0
  568. package/dist/security-runtime/licensing/candidates/rule-license-evidence.json +376 -0
  569. package/dist/security-runtime/licensing/candidates/runner-image-composition.json +1500 -0
  570. package/dist/security-runtime/licensing/candidates/selected-runtime-coverage.json +91 -0
  571. package/dist/security-runtime/licensing/candidates/yara-forge-20260927.json +58502 -0
  572. package/dist/security-runtime/licensing/commercial-components.json +379 -0
  573. package/dist/security-runtime/licensing/notices/betterleaks-1.9.0-MIT.txt +21 -0
  574. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/28a9529c7d0bb4dc51f4bf5c116a3d16ef247a052f7591466768ddf563fd1cf5.txt +428 -0
  575. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/33acf42afdd03163d75fe856cb431e70710e0b5deede3362e6d1c4697cfe1c6a.txt +28 -0
  576. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986.txt +674 -0
  577. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/4fecb8fcaf80fd9a6315bca5225ee43377015b46a0459c2925495be885cab19a.txt +21 -0
  578. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/811515f8723aa4007de2fbb75a364127fdec51cda534b87a7ea8f2fef2f96dd7.txt +24 -0
  579. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/814442c3987a2c0411171ad8ad43118e8c08f85c9c5d1dcdecc9787c7ca8689b.txt +664 -0
  580. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/8a959b2413d0c83ca1b3ce9153076c649abf2e4bbfebbb493fb24c2074a1eb8a.txt +38 -0
  581. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/995182e185856186fbbe49f150409ae026aa4cda7e94f83c6ed737cbb77fa9c7.txt +23 -0
  582. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/9b8e1305e46d1dd4011b6ed933c9f6223e08c9c4625001c5fd75a34fa930f616.txt +202 -0
  583. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/a01f1a28424bfe7f49a23d339a01847cfa9ace3864d5e7295051772a4bae2271.txt +38 -0
  584. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/a5681bf9b05db14d86776930017c647ad9e6e56ff6bbcfdf21e5848288dfaf1b.txt +165 -0
  585. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/a5b8bd9ae96c630bbf229d61c5c5def20274e00be39abfd2fab4d7879ceab462.txt +24 -0
  586. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/ada32b3747ce108d5e8bab7918af94abbd03f2fe2a46304f74c6333bbfd914ed.txt +8 -0
  587. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt +201 -0
  588. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/c738ae413cf561f174e34f6961f8ca458aae2369a73640dda6234c629b98bcc4.txt +2732 -0
  589. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/ce41c9a2c986277f7f9ab146a2195c1888a5d8da062e22d7c9c88666bb7c38cd.txt +17 -0
  590. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/d5f4c36a0528e01cfdd9e4fc60879201d3af49b41c520200643d321fe53da5f9.txt +17 -0
  591. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/e08c368ccd455f85c7fb9e04830e730f26e9dbb5c0f77bc88e09a5b0cb37df5b.txt +38 -0
  592. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/e9efb2f7daf11bb6379c06c16b870dddcecc4a10f8704bb8aad807529c8b3c97.txt +8 -0
  593. package/dist/security-runtime/licensing/notices/gitleaks-generic-origin-782f3104-MIT.txt +21 -0
  594. package/dist/security-runtime/package.json +17 -0
  595. package/dist/security-runtime/playbook/README.md +76 -0
  596. package/dist/security-runtime/playbook/access-control.md +56 -0
  597. package/dist/security-runtime/playbook/ai-features.md +53 -0
  598. package/dist/security-runtime/playbook/desktop-python.md +59 -0
  599. package/dist/security-runtime/playbook/input-handling.md +59 -0
  600. package/dist/security-runtime/playbook/mobile-expo.md +59 -0
  601. package/dist/security-runtime/playbook/payments.md +53 -0
  602. package/dist/security-runtime/playbook/privacy-account-recovery.md +59 -0
  603. package/dist/security-runtime/playbook/secrets-config.md +47 -0
  604. package/dist/security-runtime/playbook/supabase.md +48 -0
  605. package/dist/security-runtime/playbook/supply-chain.md +62 -0
  606. package/dist/security-runtime/playbook/webhooks-integrations.md +52 -0
  607. package/dist/security-runtime/rules/vsg-ai-key-in-client.yaml +41 -0
  608. package/dist/security-runtime/rules/vsg-ai-output-raw-html.yaml +35 -0
  609. package/dist/security-runtime/rules/vsg-angular-sanitizer-bypass.yaml +23 -0
  610. package/dist/security-runtime/rules/vsg-expo-token-in-asyncstorage.yaml +22 -0
  611. package/dist/security-runtime/rules/vsg-flask-public-bind.yaml +32 -0
  612. package/dist/security-runtime/rules/vsg-nextjs-action-write-no-auth.yaml +144 -0
  613. package/dist/security-runtime/rules/vsg-nextjs-inline-action-write-no-auth.yaml +161 -0
  614. package/dist/security-runtime/rules/vsg-nextjs-route-write-no-auth.yaml +193 -0
  615. package/dist/security-runtime/rules/vsg-nosql-where-injection.yaml +36 -0
  616. package/dist/security-runtime/rules/vsg-python-pickle-load.yaml +26 -0
  617. package/dist/security-runtime/rules/vsg-python-requests-verify-false.yaml +34 -0
  618. package/dist/security-runtime/rules/vsg-python-subprocess-shell.yaml +23 -0
  619. package/dist/security-runtime/rules/vsg-stripe-client-price.yaml +51 -0
  620. package/dist/security-runtime/rules/vsg-stripe-webhook-no-signature.yaml +31 -0
  621. package/dist/security-runtime/rules/vsg-stripe-webhook-pages-no-signature.yaml +51 -0
  622. package/dist/security-runtime/rules/vsg-supabase-service-role-in-client.yaml +27 -0
  623. package/dist/security-runtime/rules/vsg-supabase-service-role-public-env.yaml +24 -0
  624. package/dist/security-runtime/rules/vsg-xml-external-entities.yaml +19 -0
  625. package/dist/security-runtime/src/agent/brief.mjs +95 -0
  626. package/dist/security-runtime/src/agent/discover.mjs +108 -0
  627. package/dist/security-runtime/src/agent/exec.mjs +64 -0
  628. package/dist/security-runtime/src/agent/ingest.mjs +213 -0
  629. package/dist/security-runtime/src/agent/report.mjs +94 -0
  630. package/dist/security-runtime/src/agent/schema.mjs +62 -0
  631. package/dist/security-runtime/src/agent/tokenize.mjs +38 -0
  632. package/dist/security-runtime/src/checks/README.md +8 -0
  633. package/dist/security-runtime/src/checks/_lib/gitfiles.mjs +35 -0
  634. package/dist/security-runtime/src/checks/_lib/lockfiles.mjs +135 -0
  635. package/dist/security-runtime/src/checks/_lib/osv.mjs +6 -0
  636. package/dist/security-runtime/src/checks/_lib/secrets.mjs +381 -0
  637. package/dist/security-runtime/src/checks/_lib/targets.mjs +79 -0
  638. package/dist/security-runtime/src/checks/agent-review/index.mjs +52 -0
  639. package/dist/security-runtime/src/checks/agent-surface/config.mjs +223 -0
  640. package/dist/security-runtime/src/checks/agent-surface/index.mjs +113 -0
  641. package/dist/security-runtime/src/checks/agent-surface/instructions.mjs +87 -0
  642. package/dist/security-runtime/src/checks/agent-surface/patterns.mjs +68 -0
  643. package/dist/security-runtime/src/checks/agent-surface/unicode.mjs +88 -0
  644. package/dist/security-runtime/src/checks/code/auth-helpers.mjs +659 -0
  645. package/dist/security-runtime/src/checks/code/index.mjs +617 -0
  646. package/dist/security-runtime/src/checks/code/opengrep-container.mjs +85 -0
  647. package/dist/security-runtime/src/checks/code/opengrep-runner.mjs +86 -0
  648. package/dist/security-runtime/src/checks/code/selected-opengrep-runner.mjs +112 -0
  649. package/dist/security-runtime/src/checks/dep-change/guarddog.mjs +142 -0
  650. package/dist/security-runtime/src/checks/dep-change/index.mjs +316 -0
  651. package/dist/security-runtime/src/checks/dep-change/registries.mjs +199 -0
  652. package/dist/security-runtime/src/checks/deps-vuln/index.mjs +305 -0
  653. package/dist/security-runtime/src/checks/desktop-config/index.mjs +112 -0
  654. package/dist/security-runtime/src/checks/dns-mail/index.mjs +71 -0
  655. package/dist/security-runtime/src/checks/iac/index.mjs +83 -0
  656. package/dist/security-runtime/src/checks/install-posture/index.mjs +123 -0
  657. package/dist/security-runtime/src/checks/licenses/index.mjs +143 -0
  658. package/dist/security-runtime/src/checks/live-basic/index.mjs +77 -0
  659. package/dist/security-runtime/src/checks/live-js-secrets/index.mjs +100 -0
  660. package/dist/security-runtime/src/checks/malware/clamav.mjs +17 -0
  661. package/dist/security-runtime/src/checks/malware/defender.mjs +43 -0
  662. package/dist/security-runtime/src/checks/malware/index.mjs +186 -0
  663. package/dist/security-runtime/src/checks/malware/targets.mjs +147 -0
  664. package/dist/security-runtime/src/checks/malware/yara-runner.mjs +201 -0
  665. package/dist/security-runtime/src/checks/malware/yara.mjs +124 -0
  666. package/dist/security-runtime/src/checks/mobile-config/index.mjs +173 -0
  667. package/dist/security-runtime/src/checks/mobile-config/mobsf.mjs +105 -0
  668. package/dist/security-runtime/src/checks/posture/index.mjs +133 -0
  669. package/dist/security-runtime/src/checks/project-scripts/index.mjs +187 -0
  670. package/dist/security-runtime/src/checks/sbom/index.mjs +98 -0
  671. package/dist/security-runtime/src/checks/secrets-build/index.mjs +63 -0
  672. package/dist/security-runtime/src/checks/secrets-history/index.mjs +112 -0
  673. package/dist/security-runtime/src/checks/secrets-tree/env.mjs +120 -0
  674. package/dist/security-runtime/src/checks/secrets-tree/index.mjs +73 -0
  675. package/dist/security-runtime/src/checks/supabase-static/index.mjs +240 -0
  676. package/dist/security-runtime/src/checks/supabase-static/sql.mjs +557 -0
  677. package/dist/security-runtime/src/checks/workflows/index.mjs +93 -0
  678. package/dist/security-runtime/src/cli/deep.mjs +13 -0
  679. package/dist/security-runtime/src/cli/doctor.mjs +82 -0
  680. package/dist/security-runtime/src/cli/index.mjs +60 -0
  681. package/dist/security-runtime/src/cli/live-common.mjs +27 -0
  682. package/dist/security-runtime/src/cli/parse.mjs +56 -0
  683. package/dist/security-runtime/src/cli/portfolio.mjs +34 -0
  684. package/dist/security-runtime/src/cli/release.mjs +28 -0
  685. package/dist/security-runtime/src/cli/report.mjs +18 -0
  686. package/dist/security-runtime/src/cli/run-common.mjs +54 -0
  687. package/dist/security-runtime/src/cli/scan.mjs +12 -0
  688. package/dist/security-runtime/src/cli/setup.mjs +59 -0
  689. package/dist/security-runtime/src/cli/url.mjs +38 -0
  690. package/dist/security-runtime/src/core/accept.mjs +141 -0
  691. package/dist/security-runtime/src/core/archive.mjs +114 -0
  692. package/dist/security-runtime/src/core/base-ref.mjs +43 -0
  693. package/dist/security-runtime/src/core/checks.mjs +39 -0
  694. package/dist/security-runtime/src/core/detect.mjs +90 -0
  695. package/dist/security-runtime/src/core/errors.mjs +5 -0
  696. package/dist/security-runtime/src/core/fetch.mjs +139 -0
  697. package/dist/security-runtime/src/core/findings.mjs +177 -0
  698. package/dist/security-runtime/src/core/gate.mjs +131 -0
  699. package/dist/security-runtime/src/core/installer.mjs +255 -0
  700. package/dist/security-runtime/src/core/lock.mjs +38 -0
  701. package/dist/security-runtime/src/core/nested.mjs +107 -0
  702. package/dist/security-runtime/src/core/paths.mjs +20 -0
  703. package/dist/security-runtime/src/core/pipeline.mjs +196 -0
  704. package/dist/security-runtime/src/core/registry.mjs +81 -0
  705. package/dist/security-runtime/src/core/report.mjs +128 -0
  706. package/dist/security-runtime/src/core/runner.mjs +86 -0
  707. package/dist/security-runtime/src/core/sarif.mjs +141 -0
  708. package/dist/security-runtime/src/lattice/artifact.mjs +1201 -0
  709. package/dist/security-runtime/src/lattice/execution.mjs +45 -0
  710. package/dist/security-runtime/src/lattice/export.mjs +215 -0
  711. package/dist/security-runtime/src/lattice/provenance.mjs +70 -0
  712. package/dist/security-runtime/src/lattice/runtime.mjs +158 -0
  713. package/dist/security-runtime/src/lattice/selected-runtime.mjs +189 -0
  714. package/dist/security-runtime/src/lattice/source-acquirer.mjs +51 -0
  715. package/dist/security-runtime/src/lattice/source-fetcher.mjs +88 -0
  716. package/dist/security-runtime/src/lattice/transport.mjs +232 -0
  717. package/dist/security-runtime/src/live/analyze.mjs +101 -0
  718. package/dist/security-runtime/src/live/exposure.mjs +79 -0
  719. package/dist/security-runtime/src/live/secrets.mjs +31 -0
  720. package/dist/security-runtime/src/live/session.mjs +98 -0
  721. package/dist/security-runtime/src/live/targets.mjs +81 -0
  722. package/dist/security-runtime/src/portfolio/index.mjs +116 -0
  723. package/dist/security-runtime/tools/registry/betterleaks.json +29 -0
  724. package/dist/security-runtime/tools/registry/guarddog.json +9 -0
  725. package/dist/security-runtime/tools/registry/mobsf.json +10 -0
  726. package/dist/security-runtime/tools/registry/opengrep.json +28 -0
  727. package/dist/security-runtime/tools/registry/osv-scanner.json +14 -0
  728. package/dist/security-runtime/tools/registry/semgrep-rules.json +21 -0
  729. package/dist/security-runtime/tools/registry/trivy.json +30 -0
  730. package/dist/security-runtime/tools/registry/yara-forge-exclusions.json +14 -0
  731. package/dist/security-runtime/tools/registry/yara-forge.json +22 -0
  732. package/dist/security-runtime/tools/registry/yara-runner.json +10 -0
  733. package/dist/security-runtime/tools/registry/yara-x.json +24 -0
  734. package/dist/security-runtime/tools/registry/zizmor.json +30 -0
  735. package/dist/spinner.d.ts +16 -0
  736. package/dist/spinner.d.ts.map +1 -0
  737. package/dist/spinner.js +72 -0
  738. package/dist/spinner.js.map +1 -0
  739. package/dist/verify.test.d.ts +2 -0
  740. package/dist/verify.test.d.ts.map +1 -0
  741. package/dist/verify.test.js +98 -0
  742. package/dist/verify.test.js.map +1 -0
  743. package/package.json +73 -4
@@ -0,0 +1,305 @@
1
+ // deps-vuln (REQ-008, REQ-009): OSV-Scanner over every supported lockfile and manifest, then CISA KEV and FIRST EPSS enrichment.
2
+ // Reads lockfiles only. Never runs package managers (--no-resolve) and never installs anything.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { osvExcludeArgs } from '../_lib/osv.mjs';
6
+
7
+ export const id = 'deps-vuln';
8
+ export const title = 'Dependency vulnerabilities and known-malicious packages';
9
+ export const modes = ['scan', 'deep', 'release'];
10
+ export const requiredIn = ['scan', 'deep', 'release'];
11
+ export const timeoutSec = 600;
12
+
13
+ export const KEV_URL = 'https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json';
14
+ export const EPSS_URL = 'https://api.first.org/data/v1/epss';
15
+ const DAY = 24 * 60 * 60 * 1000;
16
+ const EPSS_BATCH = 100;
17
+ const REQUIREMENTS = /^requirements.*\.txt$/;
18
+ const base = (f) => f.slice(f.lastIndexOf('/') + 1);
19
+
20
+ export function applies(ctx) {
21
+ const d = ctx.detection;
22
+ const manifests = (d.manifests ?? []).filter((m) => REQUIREMENTS.test(base(m)) || ['go.mod', 'pom.xml'].includes(base(m)));
23
+ return d.lockfiles.length > 0 || manifests.length > 0
24
+ ? true
25
+ : { applies: false, reason: 'no lockfile or dependency manifest that OSV-Scanner supports was found' };
26
+ }
27
+
28
+ // ---- version helpers (loose: enough to place a version inside an OSV range) ----
29
+ function cmpVersion(a, b) {
30
+ const tok = (v) => String(v).toLowerCase().replace(/^v/, '').split(/[.\-+_]/).filter(Boolean);
31
+ const ta = tok(a);
32
+ const tb = tok(b);
33
+ for (let i = 0; i < Math.max(ta.length, tb.length); i++) {
34
+ const x = ta[i] ?? '0';
35
+ const y = tb[i] ?? '0';
36
+ const nx = /^\d+$/.test(x);
37
+ const ny = /^\d+$/.test(y);
38
+ if (nx && ny) { if (Number(x) !== Number(y)) return Number(x) - Number(y); } else if (nx !== ny) return nx ? 1 : -1; else if (x !== y) return x < y ? -1 : 1;
39
+ }
40
+ return 0;
41
+ }
42
+
43
+ /** Fixed versions from the ranges that contain the installed version. */
44
+ export function fixedVersions(vuln, pkg) {
45
+ const fixed = new Set();
46
+ for (const a of vuln?.affected ?? []) {
47
+ if (a.package?.name?.toLowerCase() !== pkg.name.toLowerCase() || (a.package?.ecosystem && a.package.ecosystem !== pkg.ecosystem)) continue;
48
+ for (const r of a.ranges ?? []) {
49
+ if (r.type === 'GIT') continue;
50
+ let introduced = '0';
51
+ for (const e of r.events ?? []) {
52
+ if (e.introduced !== undefined) introduced = e.introduced;
53
+ if (e.fixed !== undefined && cmpVersion(pkg.version, e.fixed) < 0 && cmpVersion(pkg.version, introduced) >= 0) fixed.add(e.fixed);
54
+ }
55
+ }
56
+ }
57
+ return [...fixed].sort(cmpVersion);
58
+ }
59
+
60
+ const LABELS = { CRITICAL: 'critical', HIGH: 'high', MODERATE: 'medium', MEDIUM: 'medium', LOW: 'low' };
61
+ const RANK = ['info', 'low', 'medium', 'high', 'critical'];
62
+ function severityOf(group, vulns) {
63
+ const candidates = [];
64
+ const score = parseFloat(group.max_severity);
65
+ if (Number.isFinite(score)) candidates.push(score >= 9 ? 'critical' : score >= 7 ? 'high' : score >= 4 ? 'medium' : 'low');
66
+ for (const v of vulns) {
67
+ const l = LABELS[String(v.database_specific?.severity ?? '').toUpperCase()];
68
+ if (l) candidates.push(l);
69
+ }
70
+ return candidates.sort((a, b) => RANK.indexOf(b) - RANK.indexOf(a))[0] ?? 'medium';
71
+ }
72
+
73
+ // ---- requirements.txt ----
74
+ const normName = (n) => n.toLowerCase().replace(/[-_.]+/g, '-');
75
+
76
+ /** Returns { pinned: Set<normalized name>, unpinned: [{ name, line, text }] } for one requirements file. */
77
+ export function parseRequirements(text) {
78
+ const pinned = new Set();
79
+ const unpinned = [];
80
+ text.split(/\r?\n/).forEach((raw, i) => {
81
+ const line = raw.replace(/\s+#.*$/, '').replace(/^#.*$/, '').trim();
82
+ if (!line || line.startsWith('-') || /^(https?|git\+|file:)/i.test(line) || line.includes('://')) return;
83
+ const m = /^([A-Za-z0-9][A-Za-z0-9._-]*)\s*(\[[^\]]*\])?\s*(.*)$/.exec(line);
84
+ if (!m) return;
85
+ const spec = m[3].split(';')[0];
86
+ if (/(^|,)\s*===?\s*[^,*\s]+\s*($|,)/.test(spec) && !/\*/.test(spec)) pinned.add(normName(m[1]));
87
+ else unpinned.push({ name: m[1], line: i + 1, text: line });
88
+ });
89
+ return { pinned, unpinned };
90
+ }
91
+
92
+ // ---- enrichment ----
93
+ async function loadKev(ctx, notes) {
94
+ try {
95
+ const r = await ctx.cachedGet(KEV_URL, { ttlMs: DAY, retries: 2, timeoutMs: 30_000 });
96
+ const j = JSON.parse(r.body.toString('utf8'));
97
+ if (!Array.isArray(j.vulnerabilities)) throw new Error('unexpected feed shape');
98
+ if (r.degraded) notes.push(`CISA KEV feed was unreachable; used a cached copy ${Math.floor(r.ageMs / DAY)} day(s) old (degraded).`);
99
+ return { set: new Set(j.vulnerabilities.map((v) => v.cveID)), version: `${j.catalogVersion ?? 'unknown'} (${r.source})` };
100
+ } catch (e) {
101
+ notes.push(`CISA KEV enrichment skipped: ${e.message} KEV status is unknown for this run (degraded).`);
102
+ return null;
103
+ }
104
+ }
105
+
106
+ async function loadEpss(ctx, cves, notes) {
107
+ const scores = new Map();
108
+ if (!cves.length) return scores;
109
+ let failed = 0;
110
+ for (let i = 0; i < cves.length; i += EPSS_BATCH) {
111
+ const batch = cves.slice(i, i + EPSS_BATCH);
112
+ try {
113
+ const res = await ctx.fetch(`${EPSS_URL}?cve=${batch.join(',')}`, { retries: 2, timeoutMs: 30_000 });
114
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
115
+ const j = JSON.parse(res.body.toString('utf8'));
116
+ for (const row of j.data ?? []) if (Number.isFinite(Number(row.epss))) scores.set(row.cve, Number(row.epss));
117
+ } catch (e) { failed++; notes.push(`FIRST EPSS lookup failed for ${batch.length} CVE(s): ${e.message}`); }
118
+ }
119
+ if (failed) notes.push('EPSS scores are missing for some findings (degraded).');
120
+ return scores;
121
+ }
122
+
123
+ // ---- mapping ----
124
+ const relFile = (repo, p) => path.relative(path.resolve(repo), path.resolve(p)).split(path.sep).join('/');
125
+ const isCve = (s) => /^CVE-\d{4}-\d{4,}$/.test(s);
126
+
127
+ const majorOf = (v) => /^\d+/.exec(String(v).replace(/^v/i, ''))?.[0] ?? '';
128
+
129
+ /**
130
+ * The version to upgrade to: the lowest version that fixes every listed advisory, preferring each advisory's fix inside the installed
131
+ * major version. Returns { version, majorJump, unfixed (advisories with no fixed version) }.
132
+ */
133
+ export function recommendFix(advisories, installed) {
134
+ const picks = [];
135
+ const unfixed = [];
136
+ for (const a of advisories) {
137
+ if (!a.fixed.length) { unfixed.push(a); continue; }
138
+ picks.push(a.fixed.find((v) => majorOf(v) === majorOf(installed)) ?? a.fixed[0]);
139
+ }
140
+ const version = picks.sort(cmpVersion).at(-1) ?? '';
141
+ return { version, majorJump: !!version && majorOf(version) !== majorOf(installed), unfixed };
142
+ }
143
+
144
+ const SEV_ORDER = ['critical', 'high', 'medium', 'low', 'info'];
145
+ const worstOf = (sevs) => SEV_ORDER.find((s) => sevs.includes(s)) ?? 'medium';
146
+
147
+ /**
148
+ * Maps parsed OSV-Scanner JSON to raw findings. `skip(file, pkg)` suppresses matches against guessed (unpinned) versions.
149
+ * One finding per ecosystem, package and version (REQ-037): every advisory, every lockfile that holds it and one recommended fix.
150
+ * MAL- known-malicious packages stay separate findings, one per lockfile.
151
+ */
152
+ export function mapOsv(osv, repo, { skip = () => false } = {}) {
153
+ const findings = [];
154
+ const packages = new Map();
155
+ for (const result of osv?.results ?? []) {
156
+ const file = relFile(repo, result.source?.path ?? '');
157
+ for (const p of result.packages ?? []) {
158
+ const pkg = p.package;
159
+ if (skip(file, pkg)) continue;
160
+ const byId = new Map((p.vulnerabilities ?? []).map((v) => [v.id, v]));
161
+ const groups = p.groups?.length ? p.groups : (p.vulnerabilities ?? []).map((v) => ({ ids: [v.id], aliases: v.aliases ?? [], max_severity: '' }));
162
+ for (const g of groups) {
163
+ const ids = [...new Set(g.ids)].sort();
164
+ const vulns = ids.map((i) => byId.get(i)).filter(Boolean);
165
+ const all = [...new Set([...ids, ...(g.aliases ?? []), ...vulns.flatMap((v) => v.aliases ?? [])])].sort();
166
+ const malId = all.find((i) => i.startsWith('MAL-'));
167
+ const primary = malId ?? ids.find((i) => i.startsWith('GHSA-')) ?? ids.find(isCve) ?? ids[0];
168
+ const summary = vulns.map((v) => v.summary).find(Boolean) ?? '';
169
+ const urls = vulns.flatMap((v) => (v.references ?? []).filter((r) => r.type === 'ADVISORY' || r.type === 'ARTICLE' || r.type === 'REPORT').map((r) => r.url));
170
+ const where = { file, package: pkg.name, version: pkg.version, ecosystem: pkg.ecosystem };
171
+ if (malId) {
172
+ const evidence = `${pkg.name}@${pkg.version} (${pkg.ecosystem}) in ${file} matches ${ids.join(', ')}${all.length > ids.length ? ` (also ${all.filter((a) => !ids.includes(a)).join(', ')})` : ''}`;
173
+ findings.push({
174
+ category: 'malicious-package', severity: 'critical', confidence: 'high', key: malId, tool: 'osv-scanner', ruleId: malId, location: where,
175
+ title: `Known malicious package: ${pkg.name}@${pkg.version}`,
176
+ description: `${summary || 'OSV lists this package version as malicious.'} This package version is in ${file}. Treat any machine, CI runner or build that installed it as compromised.`,
177
+ evidence, refs: [...new Set([...all, ...urls])].slice(0, 12),
178
+ fix: `Remove ${pkg.name}@${pkg.version} from ${file} and its manifest, reinstall from a clean state, and rotate any secrets available to machines that installed it.`,
179
+ });
180
+ continue;
181
+ }
182
+ const k = `${pkg.ecosystem}\0${pkg.name}\0${pkg.version}`;
183
+ if (!packages.has(k)) packages.set(k, { pkg, files: new Set(), advisories: new Map() });
184
+ const entry = packages.get(k);
185
+ entry.files.add(file);
186
+ if (!entry.advisories.has(primary)) {
187
+ entry.advisories.set(primary, {
188
+ primary, ids, all, summary, urls, severity: severityOf(g, vulns), fixed: [...new Set(vulns.flatMap((v) => fixedVersions(v, pkg)))].sort(cmpVersion),
189
+ cwe: vulns.flatMap((v) => v.database_specific?.cwe_ids ?? []), cves: all.filter(isCve),
190
+ });
191
+ }
192
+ }
193
+ }
194
+ }
195
+ for (const { pkg, files, advisories } of packages.values()) findings.push(packageFinding(pkg, [...files].sort(), [...advisories.values()]));
196
+ return findings;
197
+ }
198
+
199
+ /** One finding for a package version: all its advisories, lockfiles and the single recommended upgrade. */
200
+ function packageFinding(pkg, files, advisories) {
201
+ advisories.sort((a, b) => SEV_ORDER.indexOf(a.severity) - SEV_ORDER.indexOf(b.severity) || a.primary.localeCompare(b.primary));
202
+ const worst = worstOf(advisories.map((a) => a.severity));
203
+ const lead = advisories[0];
204
+ const { version: fixTo, majorJump, unfixed } = recommendFix(advisories, pkg.version);
205
+ const name = `${pkg.name}@${pkg.version}`;
206
+ const upgrade = fixTo
207
+ ? `Upgrade ${pkg.name} to ${fixTo} or later in ${files.length > 1 ? 'each of its lockfiles' : files[0]}, the lowest version that fixes ${unfixed.length ? 'every advisory that has a fix' : advisories.length === 1 ? 'the advisory' : `all ${advisories.length} advisories`}.${majorJump ? ` This is a major-version jump (${majorOf(pkg.version)}.x to ${majorOf(fixTo)}.x): read the changelog and test before merging.` : ''}${unfixed.length ? ` No fixed version is published yet for ${unfixed.map((a) => a.primary).join(', ')}.` : ''}`
208
+ : '';
209
+ // The gate blocks High only when a fix exists, so the fix field stays empty unless a High or Critical advisory is fixable.
210
+ const blockingFixable = advisories.some((a) => ['critical', 'high'].includes(a.severity) && a.fixed.length);
211
+ const policyFix = worst === 'high' && !blockingFixable ? '' : upgrade;
212
+ const lines = advisories.slice(0, 40).map((a) => `- ${a.primary}${a.cves.length && !a.primary.startsWith('CVE-') ? ` (${a.cves.join(', ')})` : ''} [${a.severity}]${a.summary ? ` ${a.summary}` : ''}; ${a.fixed.length ? `fixed in ${a.fixed[0]}` : 'no fixed version'}`);
213
+ if (advisories.length > 40) lines.push(`- and ${advisories.length - 40} more`);
214
+ const ids = [...new Set(advisories.flatMap((a) => [...a.ids, ...a.all]))];
215
+ const urls = [...new Set(advisories.flatMap((a) => a.urls))].slice(0, 12);
216
+ const single = advisories.length === 1;
217
+ return {
218
+ category: 'dependency-vuln', severity: worst, confidence: 'high', key: `${pkg.ecosystem}:${pkg.name}@${pkg.version}`, tool: 'osv-scanner', ruleId: lead.primary,
219
+ location: { file: files[0], package: pkg.name, version: pkg.version, ecosystem: pkg.ecosystem },
220
+ cwe: [...new Set(advisories.flatMap((a) => a.cwe))],
221
+ title: single ? `${name} has a known vulnerability: ${lead.primary}${lead.summary ? ` (${lead.summary})` : ''}`
222
+ : `${name} has ${advisories.length} known vulnerabilities (worst ${worst}): ${lead.primary} and ${advisories.length - 1} more`,
223
+ description: `${single ? '' : `${advisories.length} advisories, worst severity ${worst}. `}${upgrade || 'No fixed version is published yet. Replace or remove the package, or pin a patched fork.'}`,
224
+ evidence: `${name} (${pkg.ecosystem}) in ${files.join(', ')}\n${lines.join('\n')}`,
225
+ refs: [...ids, ...urls], fix: policyFix,
226
+ _cves: [...new Set(advisories.flatMap((a) => a.cves))],
227
+ };
228
+ }
229
+
230
+ function unpinnedFindings(repo, files) {
231
+ const out = [];
232
+ const pinnedByFile = new Map();
233
+ for (const file of files) {
234
+ let text;
235
+ try { text = fs.readFileSync(path.join(repo, file), 'utf8'); } catch { continue; }
236
+ const { pinned, unpinned } = parseRequirements(text);
237
+ pinnedByFile.set(file, { pinned, unpinned: new Set(unpinned.map((u) => normName(u.name))) });
238
+ if (!unpinned.length) continue;
239
+ out.push({
240
+ category: 'dependency-vuln', severity: 'medium', confidence: 'medium', key: 'unpinned-requirements', tool: 'vsg', ruleId: 'unpinned-requirements',
241
+ location: { file, ecosystem: 'PyPI' },
242
+ title: `Unpinned Python requirements in ${file} (${unpinned.length})`,
243
+ description: 'These requirements do not pin an exact version with ==, so vulnerabilities cannot be matched precisely and the installed version can change between builds. Vulnerability results for these packages are not reported.',
244
+ evidence: unpinned.map((u) => `line ${u.line}: ${u.text}`).join('\n'),
245
+ refs: [], fix: `Pin each requirement to an exact version with == (or use a lock file such as pip-compile or uv.lock): ${unpinned.map((u) => u.name).join(', ')}.`,
246
+ });
247
+ }
248
+ return { findings: out, pinnedByFile };
249
+ }
250
+
251
+ export async function run(ctx) {
252
+ const res = await ctx.runTool('osv-scanner', [
253
+ // --no-ignore: detection found these lockfiles regardless of .gitignore, so OSV must read them too (otherwise a clean result could be silent).
254
+ 'scan', 'source', '-r', '--format', 'json', '--no-resolve', '--all-vulns', '--allow-no-lockfiles', '--no-ignore',
255
+ ...osvExcludeArgs(ctx),
256
+ '.',
257
+ ]);
258
+ if (res.status !== 'ran') return { status: 'not_run', reason: res.reason };
259
+ if (res.truncated) return { status: 'not_run', reason: 'OSV-Scanner output was too large to read completely.' };
260
+ // exit 0 = nothing found, exit 1 = vulnerabilities found. Anything else is a tool failure.
261
+ if (res.exitCode !== 0 && res.exitCode !== 1) {
262
+ const tail = res.stderr.trim().split(/\r?\n/).slice(-3).join(' | ');
263
+ return { status: 'not_run', reason: `OSV-Scanner failed with exit code ${res.exitCode}${tail ? `: ${tail}` : ''}. Check network access to api.osv.dev.` };
264
+ }
265
+ let osv;
266
+ try { osv = JSON.parse(res.stdout); } catch { return { status: 'not_run', reason: 'OSV-Scanner did not return valid JSON.' }; }
267
+
268
+ const reqFiles = (ctx.detection.manifests ?? []).filter((m) => REQUIREMENTS.test(base(m)));
269
+ const { findings: unpinned, pinnedByFile } = unpinnedFindings(ctx.repo, reqFiles);
270
+ // OSV reads an unpinned requirement such as "flask>=2.0" as version 2.0; that match is a guess, so it is not reported.
271
+ const skip = (file, pkg) => pkg.ecosystem === 'PyPI' && pinnedByFile.has(file) && !pinnedByFile.get(file).pinned.has(normName(pkg.name));
272
+ const findings = [...mapOsv(osv, ctx.repo, { skip }), ...unpinned];
273
+
274
+ const notes = [];
275
+ const cves = [...new Set(findings.flatMap((f) => f._cves ?? []))];
276
+ const dataVersions = {};
277
+ if (cves.length) {
278
+ const kev = await loadKev(ctx, notes);
279
+ const epss = await loadEpss(ctx, cves, notes);
280
+ if (kev) dataVersions.kev = kev.version;
281
+ dataVersions.epss = epss.size ? `${epss.size}/${cves.length} CVEs scored` : 'none';
282
+ for (const f of findings) {
283
+ if (!f._cves) continue;
284
+ const scores = f._cves.map((c) => epss.get(c)).filter((s) => s !== undefined);
285
+ f.exploited = { kev: !!kev && f._cves.some((c) => kev.set.has(c)), epss: scores.length ? Math.max(...scores) : null };
286
+ if (f.exploited.kev) f.description = `Listed in the CISA Known Exploited Vulnerabilities catalog. ${f.description}`;
287
+ }
288
+ }
289
+ for (const f of findings) delete f._cves;
290
+ if (notes.length) {
291
+ findings.push({
292
+ category: 'other', severity: 'info', confidence: 'high', key: 'enrichment-degraded', tool: 'vsg', ruleId: 'enrichment-degraded', location: {},
293
+ title: 'Exploit-data enrichment was degraded', description: notes.join(' '), evidence: notes.join('\n'), refs: [], fix: 'Re-run when the network is available.',
294
+ });
295
+ }
296
+ const packages = (osv.results ?? []).reduce((n, r) => n + (r.packages?.length ?? 0), 0);
297
+ return {
298
+ status: 'ran',
299
+ reason: notes.length ? `degraded: ${notes[0]}` : '',
300
+ findings, dataVersions, toolVersion: ctx.registry?.get('osv-scanner')?.version ?? null,
301
+ evidence: { osvExitCode: res.exitCode, vulnerablePackages: packages, enrichment: { cves: cves.length, notes }, osv },
302
+ };
303
+ }
304
+
305
+ export default { id, title, modes, requiredIn, timeoutSec, applies, run };
@@ -0,0 +1,112 @@
1
+ // desktop-config (REQ-033): packaging, code signing and updater checks for a Python Windows desktop app.
2
+ // Reads files as text only. Requirement pinning is deps-vuln's job; installer and executable malware scans are the malware check's job.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { listFiles } from '../../core/detect.mjs';
6
+ import { resolveArtifacts, walkTree } from '../malware/targets.mjs';
7
+
8
+ export const id = 'desktop-config';
9
+ export const title = 'Desktop (Python) packaging, signing and updater';
10
+ export const modes = ['deep', 'release'];
11
+ export const requiredIn = [];
12
+ export const timeoutSec = 300;
13
+
14
+ const MAX_BYTES = 1024 * 1024;
15
+ const base = (f) => f.slice(f.lastIndexOf('/') + 1);
16
+ const read = (repo, rel) => { try { const p = path.join(repo, rel); return fs.statSync(p).size > MAX_BYTES ? '' : fs.readFileSync(p, 'utf8'); } catch { return ''; } };
17
+
18
+ const PACKAGER = /\b(pyinstaller|cx[-_]?freeze|nuitka|py2exe|briefcase)\b/i;
19
+ const MANIFEST = /^(requirements.*\.txt|pyproject\.toml|Pipfile|setup\.py|setup\.cfg)$/;
20
+ const SCRIPT = /\.(?:bat|cmd|ps1|sh|ya?ml|iss|nsi|toml|cfg|spec|py|txt|md)$/i;
21
+ const SIGNING = /\b(signtool|AzureSignTool|trusted-signing|azure\/trusted-signing-action|codesign|osslsigncode|jsign|signpath|smctl)\b/i;
22
+ const TEST_PATH = /(^|\/)(tests?|__tests__|fixtures|docs?)\//i;
23
+
24
+ function packagingMarkers(repo, files) {
25
+ const hits = [];
26
+ for (const f of files) {
27
+ if (f.endsWith('.spec')) { hits.push({ file: f, marker: '.spec file' }); continue; }
28
+ if (MANIFEST.test(base(f)) || /\.(?:bat|cmd|ps1|ya?ml)$/i.test(f)) {
29
+ const m = PACKAGER.exec(read(repo, f));
30
+ if (m) hits.push({ file: f, marker: m[1].toLowerCase() });
31
+ }
32
+ }
33
+ return hits;
34
+ }
35
+
36
+ export const applies = (ctx) => {
37
+ if (!ctx.detection.python) return { applies: false, reason: 'no Python project was detected' };
38
+ return packagingMarkers(ctx.repo, listFiles(ctx.repo)).length > 0
39
+ ? true
40
+ : { applies: false, reason: 'no Python desktop packaging marker (pyinstaller, cx_Freeze, nuitka, py2exe, briefcase or a .spec file) was found' };
41
+ };
42
+
43
+ // update-check heuristics
44
+ const UPDATER = /update|upgrade|self[-_]?update|new[-_]version|latest[-_]version/i;
45
+ const DOWNLOAD = /\b(?:requests\.(?:get|post)|urllib\.request\.(?:urlretrieve|urlopen)|urlretrieve|httpx\.(?:get|stream|Client)|session\.get)\s*\(/;
46
+ const EXECUTES = /\b(?:os\.startfile|subprocess\.(?:run|Popen|call|check_call)|os\.exec\w*|os\.replace|os\.rename|shutil\.(?:move|copy\w*)|ShellExecute\w*|\.extractall)\b/;
47
+ const VERIFY = /\b(?:hashlib|sha256|sha512|hmac|minisign|nacl|gpg|pgp|ed25519|rsa|cryptography|signature|signed|checksum|verify_\w+|verify\s*\()/i; // "verify=" (TLS flag) does not count
48
+ const WINDOW = 60;
49
+
50
+ export function updaterFindings(file, text) {
51
+ if (!UPDATER.test(file) && !/def\s+\w*(?:update|upgrade)\w*\s*\(/i.test(text)) return [];
52
+ const lines = text.split(/\r?\n/);
53
+ const out = [];
54
+ lines.forEach((line, i) => {
55
+ if (out.length || line.trim().startsWith('#') || !DOWNLOAD.test(line)) return;
56
+ const near = lines.slice(Math.max(0, i - WINDOW), i + WINDOW).join('\n');
57
+ if (!EXECUTES.test(near) || VERIFY.test(near)) return;
58
+ out.push({
59
+ category: 'config', severity: 'medium', confidence: 'low', tool: 'vsg', ruleId: 'vsg/desktop/update-unverified', key: 'desktop:update-unverified',
60
+ title: 'Update code downloads a file and runs or replaces files with no visible signature or hash check',
61
+ description: 'This update routine downloads with requests, urllib or httpx and then executes, extracts or replaces files, and nothing within 60 lines of the download checks a signature or hash. Anyone who can alter the download (a compromised host, a bad TLS setting, a hijacked URL) can run code on every installation. Found by text heuristic; the agent review should read the updater end to end (playbook/desktop-python.md, item 7).',
62
+ location: { file, line: i + 1 }, evidence: `file: ${file}\nline: ${line.trim().slice(0, 160)}`,
63
+ fix: 'Publish a SHA-256 (or better, a detached signature) with each release, ship the verifying public key or pinned hash inside the app, verify the downloaded file before running or extracting it, use HTTPS with certificate checks on, and refuse versions older than the installed one.',
64
+ });
65
+ });
66
+ return out;
67
+ }
68
+
69
+ export async function run(ctx) {
70
+ const repo = ctx.repo;
71
+ const files = listFiles(repo);
72
+ const markers = packagingMarkers(repo, files);
73
+ const findings = [];
74
+
75
+ // (b) executables and installers: the malware check scans the tree, build output and configured artifacts
76
+ const art = resolveArtifacts(repo, ctx.config?.artifacts ?? []);
77
+ const tree = walkTree(repo, { skip: ['.vsg', 'node_modules', '.venv', 'venv', ...(ctx.nestedRepos ?? []).map((n) => n.path)] });
78
+ const isBin = (f) => /\.(?:exe|msi)$/i.test(f);
79
+ const built = tree.filter((f) => isBin(f) && /(^|\/)(dist|build|out|installer|release)\//i.test(f));
80
+ const configured = [...art.inside].filter(isBin);
81
+ const binaries = [...new Set([...built, ...configured])].sort();
82
+ if (binaries.length) {
83
+ findings.push({
84
+ category: 'config', severity: 'info', confidence: 'high', tool: 'vsg', ruleId: 'vsg/desktop/binaries-scanned', key: 'desktop:binaries-scanned',
85
+ title: `${binaries.length} Windows executable or installer file(s) in build output or configured artifacts`,
86
+ description: `Files: ${binaries.slice(0, 10).join(', ')}${binaries.length > 10 ? ', ...' : ''}. The malware check (Defender and YARA) scans these files; see that check's coverage entry and evidence for the engines that actually ran. Before release, also configure the installer path in vsg.config.json artifacts if it is built outside the repository.`,
87
+ location: { file: binaries[0] }, evidence: `files: ${binaries.length}`, fix: 'Confirm the malware check ran with at least one antivirus engine on the exact files you will publish.',
88
+ });
89
+ }
90
+
91
+ // (c) code signing in packaging or CI
92
+ const texts = files.filter((f) => SCRIPT.test(f) && !TEST_PATH.test(f) && !/^(README|CHANGELOG)/i.test(base(f)));
93
+ const signingFile = texts.find((f) => SIGNING.test(read(repo, f)));
94
+ if (!signingFile) {
95
+ const anchor = markers[0].file;
96
+ findings.push({
97
+ category: 'config', severity: 'low', confidence: 'medium', tool: 'vsg', ruleId: 'vsg/desktop/no-code-signing', key: 'desktop:no-code-signing',
98
+ title: 'No Windows code-signing step found in the packaging scripts or CI',
99
+ description: 'No signtool, AzureSignTool, trusted-signing, osslsigncode or codesign step appears in the repository. Unsigned Windows executables and installers trigger SmartScreen and Defender reputation warnings, and users cannot tell a tampered copy from the real one.',
100
+ location: { file: anchor }, evidence: `packaging marker: ${markers[0].marker} in ${anchor}`,
101
+ fix: 'Sign the executable and installer in the build (for example Azure Trusted Signing or signtool with a certificate held in a secrets store, never in the repository) and timestamp the signature. If signing happens outside this repository, record that in the release notes.',
102
+ });
103
+ }
104
+
105
+ // (d) unverified updater
106
+ for (const f of files.filter((x) => x.endsWith('.py') && !TEST_PATH.test(x))) findings.push(...updaterFindings(f, read(repo, f)));
107
+
108
+ const evidence = { packagingMarkers: markers, binaries, malwareCheck: 'The malware check scans these files; read its coverage entry for the engines that ran.', signingStep: signingFile ?? null };
109
+ return { status: 'ran', reason: '', findings, evidence };
110
+ }
111
+
112
+ export default { id, title, modes, requiredIn, timeoutSec, applies, run };
@@ -0,0 +1,71 @@
1
+ // dns-mail (optional, REQ-023): SPF, DMARC and CAA records for the target's registrable domain, using Node's dns module.
2
+ // Registrable domain is the last two labels, or the last three under a common two-part suffix such as co.uk. (No public suffix
3
+ // list is bundled; an unusual suffix gives a wrong domain and the check reports the domain it used.)
4
+ import dns from 'node:dns/promises';
5
+ import net from 'node:net';
6
+
7
+ export const id = 'dns-mail';
8
+ export const title = 'Mail and certificate DNS records (SPF, DMARC, CAA)';
9
+ export const modes = ['url', 'release'];
10
+ export const requiredIn = [];
11
+ export const timeoutSec = 120;
12
+
13
+ const SECOND_LEVEL = new Set(['co', 'com', 'org', 'net', 'gov', 'edu', 'ac', 'or', 'ne', 'go']);
14
+
15
+ export function registrableDomain(hostname) {
16
+ const labels = hostname.toLowerCase().replace(/\.$/, '').split('.');
17
+ if (labels.length <= 2) return labels.join('.');
18
+ const [tld, sld] = [labels.at(-1), labels.at(-2)];
19
+ return (tld.length === 2 && SECOND_LEVEL.has(sld) ? labels.slice(-3) : labels.slice(-2)).join('.');
20
+ }
21
+
22
+ const isNamed = (hostname) => !net.isIP(hostname.replace(/^\[|\]$/g, '')) && hostname.includes('.') && !/(^|\.)localhost$/.test(hostname);
23
+
24
+ export function applies(ctx) {
25
+ if (!ctx.live) return { applies: false, reason: 'no target URL was given' };
26
+ return isNamed(ctx.live.hostname) ? true : { applies: false, reason: `${ctx.live.hostname} is not a public domain name` };
27
+ }
28
+
29
+ const EMPTY = new Set(['ENODATA', 'ENOTFOUND', 'NXDOMAIN']);
30
+ async function lookup(fn, name) {
31
+ try { return await fn(name); } catch (e) { if (EMPTY.has(e.code)) return []; throw e; }
32
+ }
33
+
34
+ const live = (f) => ({ category: 'live', tool: 'dns', refs: [], confidence: 'high', ...f });
35
+
36
+ export async function run(ctx) {
37
+ const resolver = ctx.live.resolver ?? dns;
38
+ const domain = registrableDomain(ctx.live.hostname);
39
+ let txt;
40
+ let dmarc;
41
+ let caa;
42
+ try {
43
+ txt = (await lookup((n) => resolver.resolveTxt(n), domain)).map((parts) => parts.join(''));
44
+ dmarc = (await lookup((n) => resolver.resolveTxt(n), `_dmarc.${domain}`)).map((parts) => parts.join(''));
45
+ caa = await lookup((n) => resolver.resolveCaa(n), domain);
46
+ } catch (e) { return { status: 'not_run', reason: `DNS lookup for ${domain} failed: ${e.code ?? e.message}.` }; }
47
+
48
+ const findings = [];
49
+ const where = { url: `dns:${domain}` };
50
+ if (!txt.some((r) => /^v=spf1\b/i.test(r))) {
51
+ findings.push(live({ ruleId: 'spf-missing', key: 'spf-missing', severity: 'medium', title: `No SPF record for ${domain}`, location: where,
52
+ description: 'Without SPF, mail servers cannot tell which servers may send email for this domain, so spoofed mail is easier to deliver.',
53
+ evidence: `TXT records for ${domain} contain no "v=spf1" entry.`, fix: `Publish a TXT record such as "v=spf1 include:<your mail provider> -all" at ${domain}. Publish "v=spf1 -all" if the domain sends no mail.`, cwe: [290] }));
54
+ }
55
+ const policy = dmarc.map((r) => /^v=DMARC1\b/i.test(r) && /\bp\s*=\s*(\w+)/i.exec(r)?.[1]?.toLowerCase()).find(Boolean);
56
+ if (!dmarc.some((r) => /^v=DMARC1\b/i.test(r))) {
57
+ findings.push(live({ ruleId: 'dmarc-missing', key: 'dmarc-missing', severity: 'medium', title: `No DMARC record for ${domain}`, location: where,
58
+ description: 'Without DMARC, receivers have no instruction on what to do with mail that fails SPF and DKIM, so the domain can be spoofed in phishing.',
59
+ evidence: `No "v=DMARC1" TXT record at _dmarc.${domain}.`, fix: `Publish a TXT record at _dmarc.${domain}, starting with "v=DMARC1; p=none; rua=mailto:<address>" and moving to p=quarantine and then p=reject.`, cwe: [290] }));
60
+ } else if (policy === 'none') {
61
+ findings.push(live({ ruleId: 'dmarc-none', key: 'dmarc-none', severity: 'low', title: `DMARC policy for ${domain} is p=none`, location: where,
62
+ description: 'The policy only monitors. Spoofed mail is still delivered.', evidence: `DMARC record at _dmarc.${domain} sets p=none.`, fix: 'After reviewing the reports, move to p=quarantine and then p=reject.', cwe: [290] }));
63
+ }
64
+ if (!caa.length) {
65
+ findings.push(live({ ruleId: 'caa-missing', key: 'caa-missing', severity: 'info', title: `No CAA record for ${domain}`, location: where,
66
+ description: 'A CAA record limits which certificate authorities may issue certificates for the domain.', evidence: `No CAA records at ${domain}.`, fix: `Publish CAA records at ${domain} naming the authorities you use, for example 0 issue "letsencrypt.org".`, cwe: [295] }));
67
+ }
68
+ return { status: 'ran', reason: '', findings, evidence: { 'summary.json': { domain, spf: txt.some((r) => /^v=spf1\b/i.test(r)), dmarcPolicy: policy ?? null, caaRecords: caa.length } } };
69
+ }
70
+
71
+ export default { id, title, modes, requiredIn, timeoutSec, applies, run };
@@ -0,0 +1,83 @@
1
+ // iac (REQ-022): Trivy config scanning over Dockerfiles, compose files, Kubernetes manifests and Terraform.
2
+ // `trivy config` runs only the misconfiguration scanners (vulnerabilities and secrets are covered by deps-vuln and the secrets checks).
3
+ // Image scanning of images named in config is not done: it would pull images.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { listFiles } from '../../core/detect.mjs';
7
+ import { normalizeSarif } from '../../core/sarif.mjs';
8
+
9
+ export const id = 'iac';
10
+ export const title = 'Container and infrastructure-as-code misconfiguration (Trivy)';
11
+ export const modes = ['scan', 'deep', 'release'];
12
+ export const requiredIn = [];
13
+ export const timeoutSec = 600;
14
+
15
+ const MAX_YAML_BYTES = 1_000_000;
16
+
17
+ /** A Kubernetes manifest is a YAML file with top-level apiVersion and kind. */
18
+ export function hasKubernetesManifest(repo, files = listFiles(repo)) {
19
+ return files.some((f) => {
20
+ if (!/\.ya?ml$/i.test(f) || /(^|\/)\.github\//.test(f)) return false;
21
+ try {
22
+ const full = path.join(repo, f);
23
+ if (fs.statSync(full).size > MAX_YAML_BYTES) return false;
24
+ const text = fs.readFileSync(full, 'utf8');
25
+ return /^apiVersion:/m.test(text) && /^kind:/m.test(text);
26
+ } catch { return false; }
27
+ });
28
+ }
29
+
30
+ export function applies(ctx) {
31
+ const d = ctx.detection;
32
+ if (d.docker.length || d.iac.length || hasKubernetesManifest(ctx.repo)) return true;
33
+ return { applies: false, reason: 'no Dockerfile, compose file, Kubernetes manifest or Terraform file was found' };
34
+ }
35
+
36
+ /** Maps Trivy SARIF to raw findings. Category "config": reported, never blocking (build spec blocking policy). */
37
+ export function mapTrivy(sarif, repo) {
38
+ const rules = new Map((sarif?.runs ?? []).flatMap((r) => r.tool?.driver?.rules ?? []).map((r) => [r.id, r]));
39
+ return normalizeSarif(sarif, { tool: 'trivy', category: 'iac', repo }).map((f) => {
40
+ const rule = rules.get(f.ruleId);
41
+ // Trivy's result message repeats the artifact path and says "Vulnerability"; the rule help carries the useful parts.
42
+ const message = /^Message: (.+)$/m.exec(rule?.help?.text ?? '')?.[1] ?? '';
43
+ return {
44
+ ...f,
45
+ title: `${f.title} (${f.ruleId})`,
46
+ description: rule?.fullDescription?.text ?? f.description,
47
+ fix: message || (rule?.helpUri ? `See ${rule.helpUri}.` : 'See the Trivy check documentation for this rule.'),
48
+ };
49
+ });
50
+ }
51
+
52
+ export async function run(ctx) {
53
+ // cwd is the output directory, not the repository, so a trivy.yaml or .trivyignore inside the scanned repo cannot change the scan;
54
+ // --config "" and --ignorefile "" disable both anyway. --skip-check-update keeps the run offline with the checks built into the pinned binary.
55
+ const args = [
56
+ 'config', '--format', 'sarif', '--config', '', '--ignorefile', '', '--skip-check-update', '--disable-telemetry', '--skip-version-check', '-q',
57
+ '--skip-dirs', '**/node_modules', '--skip-dirs', '**/.git', '--skip-dirs', '**/.vsg',
58
+ // nested repositories and worktrees: exact paths relative to the target (verified on 0.75.0 with a worktree fixture)
59
+ ...(ctx.nestedRepos ?? []).flatMap((n) => ['--skip-dirs', n.path]),
60
+ ctx.repo,
61
+ ];
62
+ const res = await ctx.runTool('trivy', args, { cwd: ctx.out });
63
+ if (res.status !== 'ran') return { status: 'not_run', reason: res.reason };
64
+ if (res.truncated) return { status: 'not_run', reason: 'Trivy output was too large to read completely.' };
65
+ if (res.exitCode !== 0) {
66
+ const tail = res.stderr.trim().split(/\r?\n/).slice(-3).join(' | ');
67
+ return { status: 'not_run', reason: `Trivy failed with exit code ${res.exitCode}${tail ? `: ${tail}` : ''}.` };
68
+ }
69
+ let sarif;
70
+ try { sarif = JSON.parse(res.stdout); } catch { return { status: 'not_run', reason: 'Trivy did not return valid SARIF.' }; }
71
+ const findings = mapTrivy(sarif, ctx.repo);
72
+ const version = ctx.registry?.get('trivy')?.version ?? null;
73
+ return {
74
+ status: 'ran',
75
+ reason: '',
76
+ findings,
77
+ toolVersion: version,
78
+ dataVersions: { checks: version ? `built into trivy ${version} (update skipped)` : 'built in' },
79
+ evidence: { trivyExitCode: res.exitCode, results: findings.length, imageScanning: 'not performed (would pull images)' },
80
+ };
81
+ }
82
+
83
+ export default { id, title, modes, requiredIn, timeoutSec, applies, run };