@prism-d1/cli 1.1.2 → 1.3.0

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 (255) hide show
  1. package/dist/assets/github-workflows/prism-agent-eval.yml +2 -1
  2. package/dist/assets/github-workflows/prism-eval-gate-kiro.yml +12 -2
  3. package/dist/assets/gitlab-workflows/prism-eval-gate.yml +8 -0
  4. package/dist/assets/infra/bin/app.ts +61 -37
  5. package/dist/assets/infra/lib/constructs/otel-collector-construct.ts +96 -0
  6. package/dist/assets/infra/lib/constructs/security-agent-construct.ts +35 -30
  7. package/dist/assets/infra/lib/dashboard-stack.ts +27 -0
  8. package/dist/assets/infra/lib/lambda/otel-receiver.ts +278 -8
  9. package/dist/assets/infra/lib/lambda/pricing/litellm-snapshot.json +3769 -0
  10. package/dist/assets/infra/lib/lambda/pricing/price-source.ts +56 -0
  11. package/dist/assets/infra/lib/lambda/pricing/price-table.ts +240 -0
  12. package/dist/assets/infra/lib/lambda/pricing/prices-key.ts +6 -0
  13. package/dist/assets/infra/lib/lambda/pricing/reprice.ts +108 -0
  14. package/dist/assets/infra/lib/lambda/pricing-refresh.ts +352 -0
  15. package/dist/assets/infra/lib/lambda/velocity-widget.ts +124 -1
  16. package/dist/assets/infra/lib/metrics-pipeline-stack.ts +2 -110
  17. package/dist/assets/infra/lib/security-agent-resources.ts +136 -0
  18. package/dist/assets/infra/lib/security-agent-stack.ts +69 -0
  19. package/dist/assets/infra/package.json +4 -1
  20. package/dist/assets/metric-hooks/prepare-commit-msg +3 -1
  21. package/dist/assets/pentest-cdk/bin/app.ts +12 -0
  22. package/dist/assets/pentest-cdk/cdk.json +7 -0
  23. package/dist/assets/pentest-cdk/lib/pentest-target-stack.ts +206 -0
  24. package/dist/assets/pentest-cdk/package.json +18 -0
  25. package/dist/assets/pentest-cdk/tsconfig.json +12 -0
  26. package/dist/src/codewatch/config.d.ts +17 -0
  27. package/dist/src/codewatch/config.d.ts.map +1 -0
  28. package/dist/src/codewatch/config.js +40 -0
  29. package/dist/src/codewatch/config.js.map +1 -0
  30. package/dist/src/codewatch/gitleaks.d.ts +49 -0
  31. package/dist/src/codewatch/gitleaks.d.ts.map +1 -0
  32. package/dist/src/codewatch/gitleaks.js +160 -0
  33. package/dist/src/codewatch/gitleaks.js.map +1 -0
  34. package/dist/src/codewatch/globalHooks.d.ts +52 -0
  35. package/dist/src/codewatch/globalHooks.d.ts.map +1 -0
  36. package/dist/src/codewatch/globalHooks.js +235 -0
  37. package/dist/src/codewatch/globalHooks.js.map +1 -0
  38. package/dist/src/codewatch/hooks.d.ts +38 -0
  39. package/dist/src/codewatch/hooks.d.ts.map +1 -0
  40. package/dist/src/codewatch/hooks.js +176 -0
  41. package/dist/src/codewatch/hooks.js.map +1 -0
  42. package/dist/src/codewatch/prepush.d.ts +37 -0
  43. package/dist/src/codewatch/prepush.d.ts.map +1 -0
  44. package/dist/src/codewatch/prepush.js +231 -0
  45. package/dist/src/codewatch/prepush.js.map +1 -0
  46. package/dist/src/codewatch/results.d.ts +210 -0
  47. package/dist/src/codewatch/results.d.ts.map +1 -0
  48. package/dist/src/codewatch/results.js +127 -0
  49. package/dist/src/codewatch/results.js.map +1 -0
  50. package/dist/src/codewatch/reviewers/claude.d.ts +9 -0
  51. package/dist/src/codewatch/reviewers/claude.d.ts.map +1 -0
  52. package/dist/src/codewatch/reviewers/claude.js +48 -0
  53. package/dist/src/codewatch/reviewers/claude.js.map +1 -0
  54. package/dist/src/codewatch/reviewers/index.d.ts +39 -0
  55. package/dist/src/codewatch/reviewers/index.d.ts.map +1 -0
  56. package/dist/src/codewatch/reviewers/index.js +97 -0
  57. package/dist/src/codewatch/reviewers/index.js.map +1 -0
  58. package/dist/src/codewatch/reviewers/kiro.d.ts +14 -0
  59. package/dist/src/codewatch/reviewers/kiro.d.ts.map +1 -0
  60. package/dist/src/codewatch/reviewers/kiro.js +32 -0
  61. package/dist/src/codewatch/reviewers/kiro.js.map +1 -0
  62. package/dist/src/codewatch/reviewers/types.d.ts +56 -0
  63. package/dist/src/codewatch/reviewers/types.d.ts.map +1 -0
  64. package/dist/src/codewatch/reviewers/types.js +91 -0
  65. package/dist/src/codewatch/reviewers/types.js.map +1 -0
  66. package/dist/src/codewatch/rules.d.ts +12 -0
  67. package/dist/src/codewatch/rules.d.ts.map +1 -0
  68. package/dist/src/codewatch/rules.js +27 -0
  69. package/dist/src/codewatch/rules.js.map +1 -0
  70. package/dist/src/commands/_top/dashboard.d.ts +17 -0
  71. package/dist/src/commands/_top/dashboard.d.ts.map +1 -0
  72. package/dist/src/commands/_top/dashboard.js +76 -0
  73. package/dist/src/commands/_top/dashboard.js.map +1 -0
  74. package/dist/src/commands/_top/version.d.ts +62 -0
  75. package/dist/src/commands/_top/version.d.ts.map +1 -0
  76. package/dist/src/commands/_top/version.js +175 -0
  77. package/dist/src/commands/_top/version.js.map +1 -0
  78. package/dist/src/commands/bedrock-protection/scan-account.d.ts.map +1 -1
  79. package/dist/src/commands/bedrock-protection/scan-account.js +2 -1
  80. package/dist/src/commands/bedrock-protection/scan-account.js.map +1 -1
  81. package/dist/src/commands/bedrock-protection/scan-org.d.ts.map +1 -1
  82. package/dist/src/commands/bedrock-protection/scan-org.js +16 -4
  83. package/dist/src/commands/bedrock-protection/scan-org.js.map +1 -1
  84. package/dist/src/commands/bootstrapper/install-eval-harness.d.ts.map +1 -1
  85. package/dist/src/commands/bootstrapper/install-eval-harness.js +15 -48
  86. package/dist/src/commands/bootstrapper/install-eval-harness.js.map +1 -1
  87. package/dist/src/commands/bootstrapper/install-git-hooks.d.ts +13 -0
  88. package/dist/src/commands/bootstrapper/install-git-hooks.d.ts.map +1 -1
  89. package/dist/src/commands/bootstrapper/install-git-hooks.js +28 -147
  90. package/dist/src/commands/bootstrapper/install-git-hooks.js.map +1 -1
  91. package/dist/src/commands/bootstrapper/setup-otel-sync.d.ts +12 -6
  92. package/dist/src/commands/bootstrapper/setup-otel-sync.d.ts.map +1 -1
  93. package/dist/src/commands/bootstrapper/setup-otel-sync.js +18 -443
  94. package/dist/src/commands/bootstrapper/setup-otel-sync.js.map +1 -1
  95. package/dist/src/commands/codewatch/pre-commit.d.ts +7 -0
  96. package/dist/src/commands/codewatch/pre-commit.d.ts.map +1 -0
  97. package/dist/src/commands/codewatch/pre-commit.js +30 -0
  98. package/dist/src/commands/codewatch/pre-commit.js.map +1 -0
  99. package/dist/src/commands/codewatch/pre-push.d.ts +16 -0
  100. package/dist/src/commands/codewatch/pre-push.d.ts.map +1 -0
  101. package/dist/src/commands/codewatch/pre-push.js +32 -0
  102. package/dist/src/commands/codewatch/pre-push.js.map +1 -0
  103. package/dist/src/commands/codewatch/report.d.ts +22 -0
  104. package/dist/src/commands/codewatch/report.d.ts.map +1 -0
  105. package/dist/src/commands/codewatch/report.js +64 -0
  106. package/dist/src/commands/codewatch/report.js.map +1 -0
  107. package/dist/src/commands/enable/codeburn.d.ts +59 -0
  108. package/dist/src/commands/enable/codeburn.d.ts.map +1 -0
  109. package/dist/src/commands/enable/codeburn.js +531 -0
  110. package/dist/src/commands/enable/codeburn.js.map +1 -0
  111. package/dist/src/commands/enable/codewatch.d.ts +15 -0
  112. package/dist/src/commands/enable/codewatch.d.ts.map +1 -0
  113. package/dist/src/commands/enable/codewatch.js +186 -0
  114. package/dist/src/commands/enable/codewatch.js.map +1 -0
  115. package/dist/src/commands/enable/security.d.ts +41 -0
  116. package/dist/src/commands/enable/security.d.ts.map +1 -0
  117. package/dist/src/commands/enable/security.js +130 -0
  118. package/dist/src/commands/enable/security.js.map +1 -0
  119. package/dist/src/commands/export/otel.d.ts +31 -0
  120. package/dist/src/commands/export/otel.d.ts.map +1 -0
  121. package/dist/src/commands/export/otel.js +52 -0
  122. package/dist/src/commands/export/otel.js.map +1 -0
  123. package/dist/src/commands/git/claude-session-context.d.ts.map +1 -1
  124. package/dist/src/commands/git/claude-session-context.js +2 -1
  125. package/dist/src/commands/git/claude-session-context.js.map +1 -1
  126. package/dist/src/commands/git/commit-trailers.js +1 -1
  127. package/dist/src/commands/git/commit-trailers.js.map +1 -1
  128. package/dist/src/commands/security/code-review.d.ts +22 -0
  129. package/dist/src/commands/security/code-review.d.ts.map +1 -0
  130. package/dist/src/commands/security/code-review.js +43 -0
  131. package/dist/src/commands/security/code-review.js.map +1 -0
  132. package/dist/src/commands/security/findings.d.ts +15 -0
  133. package/dist/src/commands/security/findings.d.ts.map +1 -0
  134. package/dist/src/commands/security/findings.js +31 -0
  135. package/dist/src/commands/security/findings.js.map +1 -0
  136. package/dist/src/commands/security/pentest.d.ts +18 -0
  137. package/dist/src/commands/security/pentest.d.ts.map +1 -0
  138. package/dist/src/commands/security/pentest.js +32 -0
  139. package/dist/src/commands/security/pentest.js.map +1 -0
  140. package/dist/src/commands/security/threat-model.d.ts +16 -0
  141. package/dist/src/commands/security/threat-model.d.ts.map +1 -0
  142. package/dist/src/commands/security/threat-model.js +24 -0
  143. package/dist/src/commands/security/threat-model.js.map +1 -0
  144. package/dist/src/commands/security/threats.d.ts +12 -0
  145. package/dist/src/commands/security/threats.d.ts.map +1 -0
  146. package/dist/src/commands/security/threats.js +23 -0
  147. package/dist/src/commands/security/threats.js.map +1 -0
  148. package/dist/src/commands/securityagent/setup.d.ts +19 -5
  149. package/dist/src/commands/securityagent/setup.d.ts.map +1 -1
  150. package/dist/src/commands/securityagent/setup.js +25 -189
  151. package/dist/src/commands/securityagent/setup.js.map +1 -1
  152. package/dist/src/commands/workshop/verify-setup.d.ts.map +1 -1
  153. package/dist/src/commands/workshop/verify-setup.js +5 -3
  154. package/dist/src/commands/workshop/verify-setup.js.map +1 -1
  155. package/dist/src/dashboard/codewatch.d.ts +106 -0
  156. package/dist/src/dashboard/codewatch.d.ts.map +1 -0
  157. package/dist/src/dashboard/codewatch.js +120 -0
  158. package/dist/src/dashboard/codewatch.js.map +1 -0
  159. package/dist/src/dashboard/commits.d.ts +96 -0
  160. package/dist/src/dashboard/commits.d.ts.map +1 -0
  161. package/dist/src/dashboard/commits.js +207 -0
  162. package/dist/src/dashboard/commits.js.map +1 -0
  163. package/dist/src/dashboard/page.d.ts +10 -0
  164. package/dist/src/dashboard/page.d.ts.map +1 -0
  165. package/dist/src/dashboard/page.js +470 -0
  166. package/dist/src/dashboard/page.js.map +1 -0
  167. package/dist/src/dashboard/payload.d.ts +114 -0
  168. package/dist/src/dashboard/payload.d.ts.map +1 -0
  169. package/dist/src/dashboard/payload.js +96 -0
  170. package/dist/src/dashboard/payload.js.map +1 -0
  171. package/dist/src/dashboard/report.d.ts +99 -0
  172. package/dist/src/dashboard/report.d.ts.map +1 -0
  173. package/dist/src/dashboard/report.js +300 -0
  174. package/dist/src/dashboard/report.js.map +1 -0
  175. package/dist/src/dashboard/repos.d.ts +58 -0
  176. package/dist/src/dashboard/repos.d.ts.map +1 -0
  177. package/dist/src/dashboard/repos.js +111 -0
  178. package/dist/src/dashboard/repos.js.map +1 -0
  179. package/dist/src/dashboard/security.d.ts +130 -0
  180. package/dist/src/dashboard/security.d.ts.map +1 -0
  181. package/dist/src/dashboard/security.js +144 -0
  182. package/dist/src/dashboard/security.js.map +1 -0
  183. package/dist/src/dashboard/server.d.ts +52 -0
  184. package/dist/src/dashboard/server.d.ts.map +1 -0
  185. package/dist/src/dashboard/server.js +163 -0
  186. package/dist/src/dashboard/server.js.map +1 -0
  187. package/dist/src/dashboard/setup.d.ts +67 -0
  188. package/dist/src/dashboard/setup.d.ts.map +1 -0
  189. package/dist/src/dashboard/setup.js +146 -0
  190. package/dist/src/dashboard/setup.js.map +1 -0
  191. package/dist/src/dashboard/usage.d.ts +43 -0
  192. package/dist/src/dashboard/usage.d.ts.map +1 -0
  193. package/dist/src/dashboard/usage.js +88 -0
  194. package/dist/src/dashboard/usage.js.map +1 -0
  195. package/dist/src/index.d.ts +1 -0
  196. package/dist/src/index.d.ts.map +1 -1
  197. package/dist/src/index.js +48 -2
  198. package/dist/src/index.js.map +1 -1
  199. package/dist/src/security/cfn-diff.d.ts +57 -0
  200. package/dist/src/security/cfn-diff.d.ts.map +1 -0
  201. package/dist/src/security/cfn-diff.js +112 -0
  202. package/dist/src/security/cfn-diff.js.map +1 -0
  203. package/dist/src/security/errors.d.ts +45 -0
  204. package/dist/src/security/errors.d.ts.map +1 -0
  205. package/dist/src/security/errors.js +116 -0
  206. package/dist/src/security/errors.js.map +1 -0
  207. package/dist/src/security/marker.d.ts +46 -0
  208. package/dist/src/security/marker.d.ts.map +1 -0
  209. package/dist/src/security/marker.js +120 -0
  210. package/dist/src/security/marker.js.map +1 -0
  211. package/dist/src/security/pentest-dockerfile.d.ts +8 -0
  212. package/dist/src/security/pentest-dockerfile.d.ts.map +1 -0
  213. package/dist/src/security/pentest-dockerfile.js +163 -0
  214. package/dist/src/security/pentest-dockerfile.js.map +1 -0
  215. package/dist/src/security/pentest.d.ts +233 -0
  216. package/dist/src/security/pentest.d.ts.map +1 -0
  217. package/dist/src/security/pentest.js +1057 -0
  218. package/dist/src/security/pentest.js.map +1 -0
  219. package/dist/src/security/repo.d.ts +25 -0
  220. package/dist/src/security/repo.d.ts.map +1 -0
  221. package/dist/src/security/repo.js +38 -0
  222. package/dist/src/security/repo.js.map +1 -0
  223. package/dist/src/security/review.d.ts +202 -0
  224. package/dist/src/security/review.d.ts.map +1 -0
  225. package/dist/src/security/review.js +706 -0
  226. package/dist/src/security/review.js.map +1 -0
  227. package/dist/src/security/setup.d.ts +193 -0
  228. package/dist/src/security/setup.d.ts.map +1 -0
  229. package/dist/src/security/setup.js +728 -0
  230. package/dist/src/security/setup.js.map +1 -0
  231. package/dist/src/security/threat-model.d.ts +133 -0
  232. package/dist/src/security/threat-model.d.ts.map +1 -0
  233. package/dist/src/security/threat-model.js +653 -0
  234. package/dist/src/security/threat-model.js.map +1 -0
  235. package/dist/src/utils/audit-bedrock.d.ts +1 -0
  236. package/dist/src/utils/audit-bedrock.d.ts.map +1 -1
  237. package/dist/src/utils/audit-bedrock.js +133 -0
  238. package/dist/src/utils/audit-bedrock.js.map +1 -1
  239. package/dist/src/utils/audit-org.d.ts +1 -0
  240. package/dist/src/utils/audit-org.d.ts.map +1 -1
  241. package/dist/src/utils/audit-org.js +155 -0
  242. package/dist/src/utils/audit-org.js.map +1 -1
  243. package/dist/src/utils/exec.d.ts +6 -0
  244. package/dist/src/utils/exec.d.ts.map +1 -1
  245. package/dist/src/utils/exec.js +12 -1
  246. package/dist/src/utils/exec.js.map +1 -1
  247. package/dist/src/utils/otel-sync-schedule.d.ts +41 -0
  248. package/dist/src/utils/otel-sync-schedule.d.ts.map +1 -0
  249. package/dist/src/utils/otel-sync-schedule.js +90 -0
  250. package/dist/src/utils/otel-sync-schedule.js.map +1 -0
  251. package/dist/src/utils/tool-check.d.ts +14 -0
  252. package/dist/src/utils/tool-check.d.ts.map +1 -0
  253. package/dist/src/utils/tool-check.js +42 -0
  254. package/dist/src/utils/tool-check.js.map +1 -0
  255. package/package.json +3 -2
@@ -6,7 +6,8 @@
6
6
  # - OIDC provider configured for GitHub Actions in your AWS account
7
7
  # - IAM role with bedrock:InvokeModel and events:PutEvents permissions
8
8
  # - Repository secret PRISM_METRICS_ROLE_ARN set to the OIDC role ARN
9
- # - .prism/config.json committed to the repo (created by: bash prism-cli bootstrapper install-git-hooks)
9
+ # - .prism/config.json committed to the repo with a "team_id" key; add it by hand, e.g.
10
+ # {"team_id": "team-payments"} (prism-cli enable codewatch keeps existing keys in this file)
10
11
  # - .prism/eval-harness/ installed (run: bash prism-cli bootstrapper install-eval-harness)
11
12
  # - (Optional) Agent entry point supports --mock flag for dry-run execution
12
13
 
@@ -4,7 +4,8 @@
4
4
  #
5
5
  # Required setup:
6
6
  # - Kiro API key stored as repository secret: KIRO_API_KEY
7
- # - .kiro/steering/code-review.md in your repo (review rules)
7
+ # - .prism/review/rules.md (shared with the local pre-push review) or
8
+ # .kiro/steering/code-review.md in your repo (review rules)
8
9
  # - (Optional) PRISM_METRICS_ROLE_ARN for EventBridge metrics emission
9
10
  # - (Optional) AWS Continuum configured for security scanning
10
11
  #
@@ -236,6 +237,15 @@ jobs:
236
237
  exit 0
237
238
  fi
238
239
 
240
+ # Shared rule set: .prism/review/rules.md is the one source for this
241
+ # gate and the local pre-push review (prism-cli enable codewatch).
242
+ # kiro-cli loads rules from .kiro/steering/, so sync it in for this run.
243
+ if [ -f .prism/review/rules.md ]; then
244
+ mkdir -p .kiro/steering
245
+ cp .prism/review/rules.md .kiro/steering/code-review.md
246
+ echo "Review rules: .prism/review/rules.md"
247
+ fi
248
+
239
249
  # Get all changed source files in this PR
240
250
  FILES=$(git diff --name-only "${BASE_SHA}..${HEAD_SHA}" \
241
251
  | grep -E '\.(ts|js|py|go|java|rs|tsx|jsx)$' \
@@ -638,7 +648,7 @@ jobs:
638
648
 
639
649
  if [ -z "$CODE_REVIEW_ID" ]; then
640
650
  echo "::error::Could not create the Continuum Code Review resource: ${CR_OUTPUT}"
641
- echo " Run 'prism-cli securityagent setup' to create it manually."
651
+ echo " Run 'prism-cli enable security' to create it manually."
642
652
  echo "scan_error=true" >> "$GITHUB_OUTPUT"
643
653
  echo "findings=-1" >> "$GITHUB_OUTPUT"
644
654
  exit 0
@@ -186,6 +186,14 @@ prism-eval-gate:
186
186
  elif [ -z "${BASE_SHA}" ] || [ -z "${HEAD_SHA}" ]; then
187
187
  echo "⚠️ Missing base/head SHA — review skipped."
188
188
  else
189
+ # Shared rule set (also used by the local pre-push review). kiro-cli
190
+ # loads rules from .kiro/steering/, so sync it in for this run.
191
+ if [ -f .prism/review/rules.md ]; then
192
+ mkdir -p .kiro/steering
193
+ cp .prism/review/rules.md .kiro/steering/code-review.md
194
+ echo "Review rules: .prism/review/rules.md"
195
+ fi
196
+
189
197
  FILES=$(git diff --name-only "${BASE_SHA}..${HEAD_SHA}" \
190
198
  | grep -E '\.(ts|js|py|go|java|rs|tsx|jsx)$' \
191
199
  | grep -vE '\.test\.|\.spec\.|__test__|_test\.' \
@@ -6,6 +6,7 @@ import { AwsSolutionsChecks } from 'cdk-nag';
6
6
  import { MetricsPipelineStack } from '../lib/metrics-pipeline-stack';
7
7
  import { ApiStack } from '../lib/api-stack';
8
8
  import { DashboardStack } from '../lib/dashboard-stack';
9
+ import { SecurityAgentStack } from '../lib/security-agent-stack';
9
10
 
10
11
  const app = new cdk.App();
11
12
 
@@ -14,43 +15,66 @@ const env: cdk.Environment = {
14
15
  region: process.env.CDK_DEFAULT_REGION ?? 'us-west-2',
15
16
  };
16
17
 
17
- const pipelineStack = new MetricsPipelineStack(app, 'PrismD1MetricsPipeline', {
18
- env,
19
- description: 'PRISM D1 Velocity - Core metrics event pipeline (EventBridge, DynamoDB)',
20
- tags: {
21
- 'prism:project': 'PRISM',
22
- 'prism:domain': 'D1-Velocity',
23
- 'prism:component': 'MetricsPipeline',
24
- },
25
- });
26
-
27
- const apiStack = new ApiStack(app, 'PrismD1Api', {
28
- env,
29
- description: 'PRISM D1 Velocity - Metric ingestion and query API',
30
- eventBus: pipelineStack.eventBus,
31
- eventsTable: pipelineStack.eventsTable,
32
- metadataTable: pipelineStack.metadataTable,
33
- kmsKey: pipelineStack.kmsKey,
34
- tags: {
35
- 'prism:project': 'PRISM',
36
- 'prism:domain': 'D1-Velocity',
37
- 'prism:component': 'Api',
38
- },
39
- });
40
-
41
- const dashboardStack = new DashboardStack(app, 'PrismD1Dashboard', {
42
- env,
43
- description: 'PRISM D1 Velocity - CloudWatch dashboards and alarms',
44
- productivityWidgetArn: pipelineStack.otelCollector?.productivityWidgetArn,
45
- velocityWidgetArn: pipelineStack.velocityWidgetArn,
46
- tags: {
47
- 'prism:project': 'PRISM',
48
- 'prism:domain': 'D1-Velocity',
49
- 'prism:component': 'Dashboard',
50
- },
51
- });
52
-
53
- apiStack.addDependency(pipelineStack);
18
+ // securityAgentStandalone=true builds ONLY PrismD1SecurityAgent: AWS Security
19
+ // Agent without the metrics pipeline (what `prism-cli enable security` deploys
20
+ // in an account that does not host it in the pipeline). It and
21
+ // enableSecurityAgent=true (Security Agent inside PrismD1MetricsPipeline) use
22
+ // the same resource and export names, so one account can only have one.
23
+ const standalone = app.node.tryGetContext('securityAgentStandalone') === 'true';
24
+ if (standalone && app.node.tryGetContext('enableSecurityAgent') === 'true') {
25
+ throw new Error('securityAgentStandalone=true and enableSecurityAgent=true are exclusive: Security Agent lives either in PrismD1SecurityAgent or in PrismD1MetricsPipeline, never both.');
26
+ }
27
+
28
+ if (standalone) {
29
+ new SecurityAgentStack(app, 'PrismD1SecurityAgent', {
30
+ env,
31
+ description: 'PRISM D1 Velocity - AWS Security Agent (standalone, without the metrics pipeline)',
32
+ kmsKeyArn: app.node.tryGetContext('securityAgentKmsKeyArn') as string | undefined,
33
+ tags: {
34
+ 'prism:project': 'PRISM',
35
+ 'prism:domain': 'D1-Velocity',
36
+ 'prism:component': 'SecurityAgent',
37
+ },
38
+ });
39
+ } else {
40
+ const pipelineStack = new MetricsPipelineStack(app, 'PrismD1MetricsPipeline', {
41
+ env,
42
+ description: 'PRISM D1 Velocity - Core metrics event pipeline (EventBridge, DynamoDB)',
43
+ tags: {
44
+ 'prism:project': 'PRISM',
45
+ 'prism:domain': 'D1-Velocity',
46
+ 'prism:component': 'MetricsPipeline',
47
+ },
48
+ });
49
+
50
+ const apiStack = new ApiStack(app, 'PrismD1Api', {
51
+ env,
52
+ description: 'PRISM D1 Velocity - Metric ingestion and query API',
53
+ eventBus: pipelineStack.eventBus,
54
+ eventsTable: pipelineStack.eventsTable,
55
+ metadataTable: pipelineStack.metadataTable,
56
+ kmsKey: pipelineStack.kmsKey,
57
+ tags: {
58
+ 'prism:project': 'PRISM',
59
+ 'prism:domain': 'D1-Velocity',
60
+ 'prism:component': 'Api',
61
+ },
62
+ });
63
+
64
+ const dashboardStack = new DashboardStack(app, 'PrismD1Dashboard', {
65
+ env,
66
+ description: 'PRISM D1 Velocity - CloudWatch dashboards and alarms',
67
+ productivityWidgetArn: pipelineStack.otelCollector?.productivityWidgetArn,
68
+ velocityWidgetArn: pipelineStack.velocityWidgetArn,
69
+ tags: {
70
+ 'prism:project': 'PRISM',
71
+ 'prism:domain': 'D1-Velocity',
72
+ 'prism:component': 'Dashboard',
73
+ },
74
+ });
75
+
76
+ apiStack.addDependency(pipelineStack);
77
+ }
54
78
 
55
79
  // Enable cdk-nag AWS Solutions checks on all stacks
56
80
  Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));
@@ -24,6 +24,9 @@ import * as cognito from 'aws-cdk-lib/aws-cognito';
24
24
  import * as s3 from 'aws-cdk-lib/aws-s3';
25
25
  import * as kms from 'aws-cdk-lib/aws-kms';
26
26
  import * as logs from 'aws-cdk-lib/aws-logs';
27
+ import * as events from 'aws-cdk-lib/aws-events';
28
+ import * as targets from 'aws-cdk-lib/aws-events-targets';
29
+ import * as iam from 'aws-cdk-lib/aws-iam';
27
30
  import * as apigwv2 from 'aws-cdk-lib/aws-apigatewayv2';
28
31
  import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
29
32
  import { HttpJwtAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
@@ -32,6 +35,7 @@ import * as ssm from 'aws-cdk-lib/aws-ssm';
32
35
  import * as path from 'path';
33
36
  import { Construct } from 'constructs';
34
37
  import { NagSuppressions } from 'cdk-nag';
38
+ import { PRICES_KEY } from '../lambda/pricing/prices-key';
35
39
 
36
40
  /** codeburn sync fixed loopback callback ports (see codeburn src/sync/auth.ts). */
37
41
  const CODEBURN_CALLBACK_PORTS = [19876, 19877, 19878];
@@ -509,9 +513,101 @@ export class OtelCollectorConstruct extends Construct {
509
513
 
510
514
  props.aiUsageTable.grantReadWriteData(receiver);
511
515
  this.archiveBucket.grantPut(receiver);
516
+ // The LiteLLM price table the refresh Lambda maintains (pricing/price-source.ts).
517
+ // Exact key, read only. KMS decrypt comes from grantEncryptDecrypt below.
518
+ const pricesObjectArn = this.archiveBucket.arnForObjects(PRICES_KEY);
519
+ receiver.addToRolePolicy(new iam.PolicyStatement({
520
+ actions: ['s3:GetObject'],
521
+ resources: [pricesObjectArn],
522
+ }));
512
523
  props.kmsKey.grantEncryptDecrypt(receiver);
513
524
  this.receiverFunction = receiver;
514
525
 
526
+ // -------------------------------------------------------
527
+ // Price refresh + reprice — daily
528
+ // Keeps the receiver's LiteLLM fallback table current in S3, then
529
+ // corrects spans stored at $0 whose model has since been priced.
530
+ // See lib/lambda/pricing-refresh.ts.
531
+ //
532
+ // Deliberately NOT in the VPC: the PRISM VPC has no NAT gateway, and this
533
+ // function's one job that needs a network path is an HTTPS GET to
534
+ // raw.githubusercontent.com. It accepts no inbound traffic, and reaches
535
+ // only S3 and DynamoDB besides, both over their public endpoints with IAM.
536
+ // -------------------------------------------------------
537
+ const pricingRefresh = new lambda.Function(this, 'PricingRefresh', {
538
+ functionName: 'prism-d1-pricing-refresh',
539
+ runtime: lambda.Runtime.NODEJS_22_X,
540
+ handler: 'pricing-refresh.handler',
541
+ code: lambda.Code.fromAsset(path.join(__dirname, '..', 'lambda'), {
542
+ bundling: {
543
+ image: lambda.Runtime.NODEJS_22_X.bundlingImage,
544
+ command: [
545
+ 'bash', '-c',
546
+ [
547
+ 'npm init -y > /dev/null 2>&1',
548
+ 'npm install --save @aws-sdk/client-dynamodb @aws-sdk/client-s3 esbuild > /dev/null 2>&1',
549
+ 'npx esbuild pricing-refresh.ts --bundle --platform=node --target=node22 --outfile=/asset-output/pricing-refresh.js --external:@aws-sdk/*',
550
+ ].join(' && '),
551
+ ],
552
+ local: {
553
+ tryBundle(outputDir: string): boolean {
554
+ try {
555
+ const { execSync } = require('child_process');
556
+ execSync(
557
+ `npx esbuild ${path.join(__dirname, '..', 'lambda', 'pricing-refresh.ts')} --bundle --platform=node --target=node22 --outfile=${path.join(outputDir, 'pricing-refresh.js')} --external:@aws-sdk/*`,
558
+ { stdio: 'pipe' },
559
+ );
560
+ return true;
561
+ } catch {
562
+ return false;
563
+ }
564
+ },
565
+ },
566
+ },
567
+ }),
568
+ // A full-table scan plus, on the first run, a read of the raw archive to
569
+ // recover cache-token counts for spans stored before they were kept.
570
+ timeout: cdk.Duration.minutes(15),
571
+ memorySize: 1024,
572
+ environment: {
573
+ AI_USAGE_TABLE: props.aiUsageTable.tableName,
574
+ ARCHIVE_BUCKET: this.archiveBucket.bucketName,
575
+ },
576
+ logRetention: logs.RetentionDays.ONE_MONTH,
577
+ description: 'Daily: refresh the LiteLLM price table in S3, then reprice usage spans stored at $0',
578
+ });
579
+ props.aiUsageTable.grantReadWriteData(pricingRefresh);
580
+ // Read the raw archive (to recover cache-token counts), and read/write the
581
+ // one price-table object. No delete: an old table is only ever overwritten.
582
+ this.archiveBucket.grantRead(pricingRefresh, 'otlp/*');
583
+ pricingRefresh.addToRolePolicy(new iam.PolicyStatement({
584
+ actions: ['s3:GetObject', 's3:PutObject'],
585
+ resources: [pricesObjectArn],
586
+ }));
587
+ props.kmsKey.grantEncryptDecrypt(pricingRefresh);
588
+
589
+ new events.Rule(this, 'PricingRefreshSchedule', {
590
+ ruleName: 'prism-d1-pricing-refresh-daily',
591
+ description: 'Refresh LiteLLM prices and reprice $0 spans',
592
+ // 06:17 UTC: off the hour so it is not queued behind other schedules.
593
+ schedule: events.Schedule.cron({ minute: '17', hour: '6' }),
594
+ // No retry: a failed run already repriced what it could and the next day's
595
+ // run starts from the same state. Errors surface on the Lambda metric.
596
+ targets: [new targets.LambdaFunction(pricingRefresh, { retryAttempts: 0 })],
597
+ });
598
+
599
+ NagSuppressions.addResourceSuppressions(pricingRefresh, [
600
+ {
601
+ id: 'AwsSolutions-IAM5',
602
+ reason: 'grantRead scopes S3 reads to the otlp/* prefix of the single archive bucket (raw OTLP batches, read to recover cache-token counts); grantReadWriteData adds the AI-usage table GSI index wildcard.',
603
+ appliesTo: [
604
+ 'Action::s3:GetObject*', 'Action::s3:GetBucket*', 'Action::s3:List*',
605
+ { regex: '/^Resource::<OtelCollectorArchiveBucket.*\\.Arn>\\/otlp\\/\\*$/g' },
606
+ { regex: '/^Resource::<AiUsageTable.*\\.Arn>\\/index\\/\\*$/g' },
607
+ ],
608
+ },
609
+ ], true);
610
+
515
611
  // -------------------------------------------------------
516
612
  // CloudWatch Custom Widget: Developer Productivity
517
613
  // Invoked by CloudWatch dashboards with the VIEWING user's IAM creds
@@ -168,6 +168,32 @@ export class SecurityAgentConstruct extends Construct {
168
168
  );
169
169
  }
170
170
 
171
+ // -------------------------------------------------------
172
+ // S3 Scan Bucket — stores repo ZIPs and diff files for
173
+ // API-triggered code reviews (diff scans from CI/CD) and
174
+ // threat models. Created before the agent space, which lists it.
175
+ // -------------------------------------------------------
176
+ this.scanBucket = new s3.Bucket(this, 'ScanBucket', {
177
+ bucketName: `security-agent-scans-${cdk.Aws.ACCOUNT_ID}-${cdk.Aws.REGION}`,
178
+ // SSE-S3 (not the customer-managed KMS key): the Continuum service reads
179
+ // this bucket when creating the code review and cannot access a CMK it
180
+ // has no grant on. Artifacts here are ephemeral diffs (30-day lifecycle),
181
+ // the bucket is private (BLOCK_ALL) and SSL-enforced.
182
+ encryption: s3.BucketEncryption.S3_MANAGED,
183
+ blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
184
+ enforceSSL: true,
185
+ versioned: false,
186
+ lifecycleRules: [
187
+ {
188
+ id: 'expire-scan-artifacts',
189
+ expiration: cdk.Duration.days(30),
190
+ enabled: true,
191
+ },
192
+ ],
193
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
194
+ autoDeleteObjects: true,
195
+ });
196
+
171
197
  // Agent Space
172
198
  const agentSpace = new securityagent.CfnAgentSpace(this, 'AgentSpace', {
173
199
  name: props.agentSpaceName,
@@ -177,6 +203,9 @@ export class SecurityAgentConstruct extends Construct {
177
203
  }),
178
204
  awsResources: {
179
205
  iamRoles: [this.serviceRole.roleArn],
206
+ // create-threat-model refuses S3 sources and scope docs in a bucket the
207
+ // agent space does not list ("S3 bucket not found in agent instance S3 buckets").
208
+ s3Buckets: [this.scanBucket.bucketArn],
180
209
  },
181
210
  tags: [
182
211
  { key: 'prism:component', value: 'security-agent' },
@@ -210,7 +239,7 @@ export class SecurityAgentConstruct extends Construct {
210
239
 
211
240
  // Log group for pen test results (Security Agent writes to /aws/securityagent/<space>/pt-<id>)
212
241
  // Note: we do NOT set logGroupName to avoid conflicts with pre-existing log groups
213
- // created by prism-cli securityagent setup or the Security Agent service itself.
242
+ // created by prism-cli enable security or the Security Agent service itself.
214
243
  const pentestLogGroup = logs.LogGroup.fromLogGroupName(
215
244
  this, 'PentestLogGroup',
216
245
  `/aws/securityagent/${props.agentSpaceName}`,
@@ -237,31 +266,6 @@ export class SecurityAgentConstruct extends Construct {
237
266
  });
238
267
  }
239
268
 
240
- // -------------------------------------------------------
241
- // S3 Scan Bucket — stores repo ZIPs and diff files for
242
- // API-triggered code reviews (diff scans from CI/CD).
243
- // -------------------------------------------------------
244
- this.scanBucket = new s3.Bucket(this, 'ScanBucket', {
245
- bucketName: `security-agent-scans-${cdk.Aws.ACCOUNT_ID}-${cdk.Aws.REGION}`,
246
- // SSE-S3 (not the customer-managed KMS key): the Continuum service reads
247
- // this bucket when creating the code review and cannot access a CMK it
248
- // has no grant on. Artifacts here are ephemeral diffs (30-day lifecycle),
249
- // the bucket is private (BLOCK_ALL) and SSL-enforced.
250
- encryption: s3.BucketEncryption.S3_MANAGED,
251
- blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
252
- enforceSSL: true,
253
- versioned: false,
254
- lifecycleRules: [
255
- {
256
- id: 'expire-scan-artifacts',
257
- expiration: cdk.Duration.days(30),
258
- enabled: true,
259
- },
260
- ],
261
- removalPolicy: cdk.RemovalPolicy.DESTROY,
262
- autoDeleteObjects: true,
263
- });
264
-
265
269
  // Grant Security Agent service role read access to the scan bucket
266
270
  this.scanBucket.grantRead(this.serviceRole);
267
271
 
@@ -298,10 +302,11 @@ export class SecurityAgentConstruct extends Construct {
298
302
  // Code Review resource — NOT created here in CDK.
299
303
  // CreateCodeReview requires assets.sourceCode pointing to
300
304
  // an existing ZIP of the repo (chicken-and-egg at deploy).
301
- // Instead, `prism-cli securityagent setup` handles:
302
- // 1. git archive → upload repo.zip to scan bucket
305
+ // Instead, `prism-cli enable security` (or the CI eval gate on its
306
+ // first run) handles, per repo:
307
+ // 1. git archive → upload repo/<repo-slug>/latest.zip to the scan bucket
303
308
  // 2. CreateCodeReview API call
304
- // 3. Write code-review-id to SSM
309
+ // 3. Write /prism/continuum/code-review-id/<repo-slug> to SSM
305
310
  // The CDK only provisions the bucket + role + SSM scaffold.
306
311
  // -------------------------------------------------------
307
312
 
@@ -325,7 +330,7 @@ export class SecurityAgentConstruct extends Construct {
325
330
  tier: ssm.ParameterTier.STANDARD,
326
331
  });
327
332
 
328
- // code-review-id is written by `prism-cli securityagent setup` (not CDK)
333
+ // code-review-id/<repo-slug> is written by `prism-cli enable security` (not CDK)
329
334
  // because CreateCodeReview needs an existing repo ZIP in S3 first.
330
335
 
331
336
  new ssm.StringParameter(this, 'ParamScanBucket', {
@@ -808,6 +808,33 @@ export class DashboardStack extends cdk.Stack {
808
808
  }),
809
809
  );
810
810
 
811
+ // --- Row 5: Tools the model invoked (codeburn ai.tools) ---
812
+ // Store-backed like the spend tiles: OTEL#TOOLDAY rollups are bucketed by
813
+ // the call's own day and keep full history, so a backfilled sync push is
814
+ // not cut off at CloudWatch's two-week window. Follows the Developer filter.
815
+ devDashboard.addWidgets(
816
+ new cloudwatch.CustomWidget({
817
+ functionArn: props.velocityWidgetArn ?? '',
818
+ title: 'Tool Usage: Top Tools & MCP Servers',
819
+ width: 16,
820
+ height: 9,
821
+ params: { view: 'tool-usage-top', user: USER_TOKEN },
822
+ updateOnRefresh: true,
823
+ updateOnResize: false,
824
+ updateOnTimeRangeChange: true,
825
+ }),
826
+ new cloudwatch.CustomWidget({
827
+ functionArn: props.velocityWidgetArn ?? '',
828
+ title: 'Tool Calls / Day',
829
+ width: 8,
830
+ height: 9,
831
+ params: { view: 'tool-usage-trend', user: USER_TOKEN },
832
+ updateOnRefresh: true,
833
+ updateOnResize: false,
834
+ updateOnTimeRangeChange: true,
835
+ }),
836
+ );
837
+
811
838
  new cdk.CfnOutput(this, 'DevProductivityDashboardUrl', {
812
839
  value: `https://${this.region}.console.aws.amazon.com/cloudwatch/home?region=${this.region}#dashboards:name=PRISM-D1-Developer-Productivity`,
813
840
  description: 'Developer Productivity Dashboard URL',