@prism-d1/cli 1.2.0 → 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.
- package/dist/assets/github-workflows/prism-eval-gate-kiro.yml +2 -2
- package/dist/assets/infra/bin/app.ts +61 -37
- package/dist/assets/infra/lib/constructs/security-agent-construct.ts +35 -30
- package/dist/assets/infra/lib/metrics-pipeline-stack.ts +2 -110
- package/dist/assets/infra/lib/security-agent-resources.ts +136 -0
- package/dist/assets/infra/lib/security-agent-stack.ts +69 -0
- package/dist/assets/pentest-cdk/bin/app.ts +12 -0
- package/dist/assets/pentest-cdk/cdk.json +7 -0
- package/dist/assets/pentest-cdk/lib/pentest-target-stack.ts +206 -0
- package/dist/assets/pentest-cdk/package.json +18 -0
- package/dist/assets/pentest-cdk/tsconfig.json +12 -0
- package/dist/src/codewatch/results.d.ts +124 -4
- package/dist/src/codewatch/results.d.ts.map +1 -1
- package/dist/src/codewatch/results.js +8 -2
- package/dist/src/codewatch/results.js.map +1 -1
- package/dist/src/commands/_top/version.d.ts +62 -0
- package/dist/src/commands/_top/version.d.ts.map +1 -0
- package/dist/src/commands/_top/version.js +175 -0
- package/dist/src/commands/_top/version.js.map +1 -0
- package/dist/src/commands/bootstrapper/setup-otel-sync.d.ts +12 -6
- package/dist/src/commands/bootstrapper/setup-otel-sync.d.ts.map +1 -1
- package/dist/src/commands/bootstrapper/setup-otel-sync.js +18 -409
- package/dist/src/commands/bootstrapper/setup-otel-sync.js.map +1 -1
- package/dist/src/commands/enable/codeburn.d.ts +59 -0
- package/dist/src/commands/enable/codeburn.d.ts.map +1 -0
- package/dist/src/commands/enable/codeburn.js +531 -0
- package/dist/src/commands/enable/codeburn.js.map +1 -0
- package/dist/src/commands/enable/security.d.ts +41 -0
- package/dist/src/commands/enable/security.d.ts.map +1 -0
- package/dist/src/commands/enable/security.js +130 -0
- package/dist/src/commands/enable/security.js.map +1 -0
- package/dist/src/commands/export/otel.d.ts +31 -0
- package/dist/src/commands/export/otel.d.ts.map +1 -0
- package/dist/src/commands/export/otel.js +52 -0
- package/dist/src/commands/export/otel.js.map +1 -0
- package/dist/src/commands/security/code-review.d.ts +22 -0
- package/dist/src/commands/security/code-review.d.ts.map +1 -0
- package/dist/src/commands/security/code-review.js +43 -0
- package/dist/src/commands/security/code-review.js.map +1 -0
- package/dist/src/commands/security/findings.d.ts +15 -0
- package/dist/src/commands/security/findings.d.ts.map +1 -0
- package/dist/src/commands/security/findings.js +31 -0
- package/dist/src/commands/security/findings.js.map +1 -0
- package/dist/src/commands/security/pentest.d.ts +18 -0
- package/dist/src/commands/security/pentest.d.ts.map +1 -0
- package/dist/src/commands/security/pentest.js +32 -0
- package/dist/src/commands/security/pentest.js.map +1 -0
- package/dist/src/commands/security/threat-model.d.ts +16 -0
- package/dist/src/commands/security/threat-model.d.ts.map +1 -0
- package/dist/src/commands/security/threat-model.js +24 -0
- package/dist/src/commands/security/threat-model.js.map +1 -0
- package/dist/src/commands/security/threats.d.ts +12 -0
- package/dist/src/commands/security/threats.d.ts.map +1 -0
- package/dist/src/commands/security/threats.js +23 -0
- package/dist/src/commands/security/threats.js.map +1 -0
- package/dist/src/commands/securityagent/setup.d.ts +19 -5
- package/dist/src/commands/securityagent/setup.d.ts.map +1 -1
- package/dist/src/commands/securityagent/setup.js +25 -189
- package/dist/src/commands/securityagent/setup.js.map +1 -1
- package/dist/src/dashboard/codewatch.d.ts.map +1 -1
- package/dist/src/dashboard/codewatch.js +2 -1
- package/dist/src/dashboard/codewatch.js.map +1 -1
- package/dist/src/dashboard/page.d.ts.map +1 -1
- package/dist/src/dashboard/page.js +159 -4
- package/dist/src/dashboard/page.js.map +1 -1
- package/dist/src/dashboard/payload.d.ts +8 -5
- package/dist/src/dashboard/payload.d.ts.map +1 -1
- package/dist/src/dashboard/payload.js +5 -2
- package/dist/src/dashboard/payload.js.map +1 -1
- package/dist/src/dashboard/security.d.ts +130 -0
- package/dist/src/dashboard/security.d.ts.map +1 -0
- package/dist/src/dashboard/security.js +144 -0
- package/dist/src/dashboard/security.js.map +1 -0
- package/dist/src/dashboard/setup.d.ts +15 -0
- package/dist/src/dashboard/setup.d.ts.map +1 -1
- package/dist/src/dashboard/setup.js +20 -3
- package/dist/src/dashboard/setup.js.map +1 -1
- package/dist/src/dashboard/usage.js +1 -1
- package/dist/src/dashboard/usage.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/security/cfn-diff.d.ts +57 -0
- package/dist/src/security/cfn-diff.d.ts.map +1 -0
- package/dist/src/security/cfn-diff.js +112 -0
- package/dist/src/security/cfn-diff.js.map +1 -0
- package/dist/src/security/errors.d.ts +45 -0
- package/dist/src/security/errors.d.ts.map +1 -0
- package/dist/src/security/errors.js +116 -0
- package/dist/src/security/errors.js.map +1 -0
- package/dist/src/security/marker.d.ts +46 -0
- package/dist/src/security/marker.d.ts.map +1 -0
- package/dist/src/security/marker.js +120 -0
- package/dist/src/security/marker.js.map +1 -0
- package/dist/src/security/pentest-dockerfile.d.ts +8 -0
- package/dist/src/security/pentest-dockerfile.d.ts.map +1 -0
- package/dist/src/security/pentest-dockerfile.js +163 -0
- package/dist/src/security/pentest-dockerfile.js.map +1 -0
- package/dist/src/security/pentest.d.ts +233 -0
- package/dist/src/security/pentest.d.ts.map +1 -0
- package/dist/src/security/pentest.js +1057 -0
- package/dist/src/security/pentest.js.map +1 -0
- package/dist/src/security/repo.d.ts +25 -0
- package/dist/src/security/repo.d.ts.map +1 -0
- package/dist/src/security/repo.js +38 -0
- package/dist/src/security/repo.js.map +1 -0
- package/dist/src/security/review.d.ts +202 -0
- package/dist/src/security/review.d.ts.map +1 -0
- package/dist/src/security/review.js +706 -0
- package/dist/src/security/review.js.map +1 -0
- package/dist/src/security/setup.d.ts +193 -0
- package/dist/src/security/setup.d.ts.map +1 -0
- package/dist/src/security/setup.js +728 -0
- package/dist/src/security/setup.js.map +1 -0
- package/dist/src/security/threat-model.d.ts +133 -0
- package/dist/src/security/threat-model.d.ts.map +1 -0
- package/dist/src/security/threat-model.js +653 -0
- package/dist/src/security/threat-model.js.map +1 -0
- package/dist/src/utils/exec.d.ts +6 -0
- package/dist/src/utils/exec.d.ts.map +1 -1
- package/dist/src/utils/exec.js +12 -1
- package/dist/src/utils/exec.js.map +1 -1
- package/dist/src/utils/otel-sync-schedule.d.ts +19 -0
- package/dist/src/utils/otel-sync-schedule.d.ts.map +1 -1
- package/dist/src/utils/otel-sync-schedule.js +24 -2
- package/dist/src/utils/otel-sync-schedule.js.map +1 -1
- package/package.json +3 -3
|
@@ -238,7 +238,7 @@ jobs:
|
|
|
238
238
|
fi
|
|
239
239
|
|
|
240
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
|
|
241
|
+
# gate and the local pre-push review (prism-cli enable codewatch).
|
|
242
242
|
# kiro-cli loads rules from .kiro/steering/, so sync it in for this run.
|
|
243
243
|
if [ -f .prism/review/rules.md ]; then
|
|
244
244
|
mkdir -p .kiro/steering
|
|
@@ -648,7 +648,7 @@ jobs:
|
|
|
648
648
|
|
|
649
649
|
if [ -z "$CODE_REVIEW_ID" ]; then
|
|
650
650
|
echo "::error::Could not create the Continuum Code Review resource: ${CR_OUTPUT}"
|
|
651
|
-
echo " Run 'prism-cli
|
|
651
|
+
echo " Run 'prism-cli enable security' to create it manually."
|
|
652
652
|
echo "scan_error=true" >> "$GITHUB_OUTPUT"
|
|
653
653
|
echo "findings=-1" >> "$GITHUB_OUTPUT"
|
|
654
654
|
exit 0
|
|
@@ -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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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 }));
|
|
@@ -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
|
|
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
|
|
302
|
-
//
|
|
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
|
|
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', {
|
|
@@ -15,6 +15,7 @@ import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
|
15
15
|
import { PrismVpcConstruct } from './constructs/prism-vpc-construct';
|
|
16
16
|
import { GuardrailEnforcerConstruct } from './constructs/guardrail-enforcer-construct';
|
|
17
17
|
import { SecurityAgentConstruct } from './constructs/security-agent-construct';
|
|
18
|
+
import { addSecurityAgentResources } from './security-agent-resources';
|
|
18
19
|
import { OtelCollectorConstruct } from './constructs/otel-collector-construct';
|
|
19
20
|
import { NagSuppressions } from 'cdk-nag';
|
|
20
21
|
|
|
@@ -613,116 +614,7 @@ export class MetricsPipelineStack extends cdk.Stack {
|
|
|
613
614
|
// -------------------------------------------------------
|
|
614
615
|
const enableSecurityAgent = this.node.tryGetContext('enableSecurityAgent') === 'true';
|
|
615
616
|
if (enableSecurityAgent) {
|
|
616
|
-
this.securityAgent =
|
|
617
|
-
agentSpaceName: 'prism-d1-security',
|
|
618
|
-
description: 'PRISM D1 Continuum space for design review, code review, and pen testing',
|
|
619
|
-
kmsKey: prismKmsKey,
|
|
620
|
-
codeRemediationStrategy: 'DISABLED',
|
|
621
|
-
tags: {
|
|
622
|
-
'prism:pillar': 'security',
|
|
623
|
-
},
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
// Grant GitHub OIDC roles access to scan bucket and Continuum APIs.
|
|
627
|
-
// The OIDC role is created by `prism-cli bootstrapper setup-github-oidc`
|
|
628
|
-
// with name pattern prism-d1-github-oidc-*.
|
|
629
|
-
this.securityAgent.scanBucket.addToResourcePolicy(
|
|
630
|
-
new iam.PolicyStatement({
|
|
631
|
-
effect: iam.Effect.ALLOW,
|
|
632
|
-
principals: [new iam.ArnPrincipal(`arn:aws:iam::${cdk.Aws.ACCOUNT_ID}:root`)],
|
|
633
|
-
actions: ['s3:PutObject', 's3:GetObject'],
|
|
634
|
-
resources: [this.securityAgent.scanBucket.arnForObjects('*')],
|
|
635
|
-
conditions: {
|
|
636
|
-
ArnLike: {
|
|
637
|
-
'aws:PrincipalArn': `arn:aws:iam::${cdk.Aws.ACCOUNT_ID}:role/prism-d1-github-oidc-*`,
|
|
638
|
-
},
|
|
639
|
-
},
|
|
640
|
-
}),
|
|
641
|
-
);
|
|
642
|
-
|
|
643
|
-
// Create a managed policy for Continuum scan API access.
|
|
644
|
-
// Attached to the OIDC role by the bootstrapper setup script.
|
|
645
|
-
const continuumCiPolicy = new iam.ManagedPolicy(this, 'ContinuumCiPolicy', {
|
|
646
|
-
managedPolicyName: 'prism-d1-continuum-ci-scan',
|
|
647
|
-
description: 'Grants GitHub Actions workflows access to Continuum diff scan APIs and SSM config',
|
|
648
|
-
statements: [
|
|
649
|
-
new iam.PolicyStatement({
|
|
650
|
-
sid: 'ContinuumScanAPIs',
|
|
651
|
-
effect: iam.Effect.ALLOW,
|
|
652
|
-
actions: [
|
|
653
|
-
'securityagent:StartCodeReviewJob',
|
|
654
|
-
'securityagent:BatchGetCodeReviewJobs',
|
|
655
|
-
'securityagent:BatchGetCodeReviewJobTasks',
|
|
656
|
-
'securityagent:ListFindings',
|
|
657
|
-
'securityagent:BatchGetFindings',
|
|
658
|
-
'securityagent:CreateCodeReview',
|
|
659
|
-
'securityagent:ListCodeReviews',
|
|
660
|
-
],
|
|
661
|
-
resources: [
|
|
662
|
-
`arn:aws:securityagent:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:agent-space/*`,
|
|
663
|
-
],
|
|
664
|
-
}),
|
|
665
|
-
new iam.PolicyStatement({
|
|
666
|
-
sid: 'PassRoleForCodeReview',
|
|
667
|
-
effect: iam.Effect.ALLOW,
|
|
668
|
-
actions: ['iam:PassRole'],
|
|
669
|
-
resources: [this.securityAgent.serviceRole.roleArn],
|
|
670
|
-
conditions: {
|
|
671
|
-
StringEquals: { 'iam:PassedToService': 'securityagent.amazonaws.com' },
|
|
672
|
-
},
|
|
673
|
-
}),
|
|
674
|
-
new iam.PolicyStatement({
|
|
675
|
-
sid: 'ScanBucketWrite',
|
|
676
|
-
effect: iam.Effect.ALLOW,
|
|
677
|
-
actions: ['s3:PutObject', 's3:GetObject', 's3:ListBucket'],
|
|
678
|
-
resources: [
|
|
679
|
-
this.securityAgent.scanBucket.bucketArn,
|
|
680
|
-
this.securityAgent.scanBucket.arnForObjects('*'),
|
|
681
|
-
],
|
|
682
|
-
}),
|
|
683
|
-
new iam.PolicyStatement({
|
|
684
|
-
sid: 'KMSForScanBucket',
|
|
685
|
-
effect: iam.Effect.ALLOW,
|
|
686
|
-
actions: ['kms:Encrypt', 'kms:Decrypt', 'kms:GenerateDataKey*', 'kms:DescribeKey'],
|
|
687
|
-
resources: [prismKmsKey.keyArn],
|
|
688
|
-
}),
|
|
689
|
-
new iam.PolicyStatement({
|
|
690
|
-
sid: 'SSMReadConfig',
|
|
691
|
-
effect: iam.Effect.ALLOW,
|
|
692
|
-
actions: ['ssm:GetParameter', 'ssm:GetParameters', 'ssm:PutParameter'],
|
|
693
|
-
resources: [
|
|
694
|
-
`arn:aws:ssm:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:parameter/prism/continuum/*`,
|
|
695
|
-
],
|
|
696
|
-
}),
|
|
697
|
-
],
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
NagSuppressions.addResourceSuppressions(
|
|
701
|
-
continuumCiPolicy,
|
|
702
|
-
[
|
|
703
|
-
{
|
|
704
|
-
id: 'AwsSolutions-IAM5',
|
|
705
|
-
reason: 'Agent space ID is dynamic; wildcard scoped to securityagent service resources only',
|
|
706
|
-
appliesTo: [`Resource::arn:aws:securityagent:<AWS::Region>:<AWS::AccountId>:agent-space/*`],
|
|
707
|
-
},
|
|
708
|
-
{
|
|
709
|
-
id: 'AwsSolutions-IAM5',
|
|
710
|
-
reason: 'Scan bucket objects include dynamic diff filenames (SHA-based); wildcard on objects only',
|
|
711
|
-
appliesTo: ['Resource::<SecurityAgentScanBucketB752F4A9.Arn>/*'],
|
|
712
|
-
},
|
|
713
|
-
{
|
|
714
|
-
id: 'AwsSolutions-IAM5',
|
|
715
|
-
reason: 'SSM parameters under /prism/continuum/ are all non-sensitive config values (IDs and bucket names)',
|
|
716
|
-
appliesTo: ['Resource::arn:aws:ssm:<AWS::Region>:<AWS::AccountId>:parameter/prism/continuum/*'],
|
|
717
|
-
},
|
|
718
|
-
],
|
|
719
|
-
true,
|
|
720
|
-
);
|
|
721
|
-
|
|
722
|
-
new cdk.CfnOutput(this, 'ContinuumCiPolicyArn', {
|
|
723
|
-
value: continuumCiPolicy.managedPolicyArn,
|
|
724
|
-
description: 'Attach this policy to your GitHub OIDC role for Continuum CI scans',
|
|
725
|
-
});
|
|
617
|
+
this.securityAgent = addSecurityAgentResources(this, prismKmsKey);
|
|
726
618
|
}
|
|
727
619
|
|
|
728
620
|
// -------------------------------------------------------
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Security Agent resources and the CI access to them, shared by the two
|
|
3
|
+
* places they can live:
|
|
4
|
+
*
|
|
5
|
+
* - PrismD1MetricsPipeline, with context enableSecurityAgent=true (the
|
|
6
|
+
* original layout; existing accounts keep it);
|
|
7
|
+
* - PrismD1SecurityAgent, the standalone stack (context
|
|
8
|
+
* securityAgentStandalone=true), for accounts that want Security Agent
|
|
9
|
+
* without the metrics pipeline.
|
|
10
|
+
*
|
|
11
|
+
* Construct ids are relative to the given scope, so the pipeline's logical ids
|
|
12
|
+
* are unchanged by moving this code here. Both layouts use the same fixed names
|
|
13
|
+
* (role, managed policy, bucket, SSM parameters, exports), so CloudFormation
|
|
14
|
+
* refuses a second copy in the same account and region.
|
|
15
|
+
*/
|
|
16
|
+
import * as cdk from 'aws-cdk-lib';
|
|
17
|
+
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
18
|
+
import * as kms from 'aws-cdk-lib/aws-kms';
|
|
19
|
+
import { Construct } from 'constructs';
|
|
20
|
+
import { NagSuppressions } from 'cdk-nag';
|
|
21
|
+
import { SecurityAgentConstruct } from './constructs/security-agent-construct';
|
|
22
|
+
|
|
23
|
+
export function addSecurityAgentResources(scope: Construct, kmsKey: kms.IKey): SecurityAgentConstruct {
|
|
24
|
+
const securityAgent = new SecurityAgentConstruct(scope, 'SecurityAgent', {
|
|
25
|
+
agentSpaceName: 'prism-d1-security',
|
|
26
|
+
description: 'PRISM D1 Continuum space for design review, code review, and pen testing',
|
|
27
|
+
kmsKey: kmsKey,
|
|
28
|
+
codeRemediationStrategy: 'DISABLED',
|
|
29
|
+
tags: {
|
|
30
|
+
'prism:pillar': 'security',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Grant GitHub OIDC roles access to scan bucket and Continuum APIs.
|
|
35
|
+
// The OIDC role is created by `prism-cli bootstrapper setup-github-oidc`
|
|
36
|
+
// with name pattern prism-d1-github-oidc-*.
|
|
37
|
+
securityAgent.scanBucket.addToResourcePolicy(
|
|
38
|
+
new iam.PolicyStatement({
|
|
39
|
+
effect: iam.Effect.ALLOW,
|
|
40
|
+
principals: [new iam.ArnPrincipal(`arn:aws:iam::${cdk.Aws.ACCOUNT_ID}:root`)],
|
|
41
|
+
actions: ['s3:PutObject', 's3:GetObject'],
|
|
42
|
+
resources: [securityAgent.scanBucket.arnForObjects('*')],
|
|
43
|
+
conditions: {
|
|
44
|
+
ArnLike: {
|
|
45
|
+
'aws:PrincipalArn': `arn:aws:iam::${cdk.Aws.ACCOUNT_ID}:role/prism-d1-github-oidc-*`,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Create a managed policy for Continuum scan API access.
|
|
52
|
+
// Attached to the OIDC role by the bootstrapper setup script.
|
|
53
|
+
const continuumCiPolicy = new iam.ManagedPolicy(scope, 'ContinuumCiPolicy', {
|
|
54
|
+
managedPolicyName: 'prism-d1-continuum-ci-scan',
|
|
55
|
+
description: 'Grants GitHub Actions workflows access to Continuum diff scan APIs and SSM config',
|
|
56
|
+
statements: [
|
|
57
|
+
new iam.PolicyStatement({
|
|
58
|
+
sid: 'ContinuumScanAPIs',
|
|
59
|
+
effect: iam.Effect.ALLOW,
|
|
60
|
+
actions: [
|
|
61
|
+
'securityagent:StartCodeReviewJob',
|
|
62
|
+
'securityagent:BatchGetCodeReviewJobs',
|
|
63
|
+
'securityagent:BatchGetCodeReviewJobTasks',
|
|
64
|
+
'securityagent:ListFindings',
|
|
65
|
+
'securityagent:BatchGetFindings',
|
|
66
|
+
'securityagent:CreateCodeReview',
|
|
67
|
+
'securityagent:ListCodeReviews',
|
|
68
|
+
],
|
|
69
|
+
resources: [
|
|
70
|
+
`arn:aws:securityagent:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:agent-space/*`,
|
|
71
|
+
],
|
|
72
|
+
}),
|
|
73
|
+
new iam.PolicyStatement({
|
|
74
|
+
sid: 'PassRoleForCodeReview',
|
|
75
|
+
effect: iam.Effect.ALLOW,
|
|
76
|
+
actions: ['iam:PassRole'],
|
|
77
|
+
resources: [securityAgent.serviceRole.roleArn],
|
|
78
|
+
conditions: {
|
|
79
|
+
StringEquals: { 'iam:PassedToService': 'securityagent.amazonaws.com' },
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
new iam.PolicyStatement({
|
|
83
|
+
sid: 'ScanBucketWrite',
|
|
84
|
+
effect: iam.Effect.ALLOW,
|
|
85
|
+
actions: ['s3:PutObject', 's3:GetObject', 's3:ListBucket'],
|
|
86
|
+
resources: [
|
|
87
|
+
securityAgent.scanBucket.bucketArn,
|
|
88
|
+
securityAgent.scanBucket.arnForObjects('*'),
|
|
89
|
+
],
|
|
90
|
+
}),
|
|
91
|
+
new iam.PolicyStatement({
|
|
92
|
+
sid: 'KMSForScanBucket',
|
|
93
|
+
effect: iam.Effect.ALLOW,
|
|
94
|
+
actions: ['kms:Encrypt', 'kms:Decrypt', 'kms:GenerateDataKey*', 'kms:DescribeKey'],
|
|
95
|
+
resources: [kmsKey.keyArn],
|
|
96
|
+
}),
|
|
97
|
+
new iam.PolicyStatement({
|
|
98
|
+
sid: 'SSMReadConfig',
|
|
99
|
+
effect: iam.Effect.ALLOW,
|
|
100
|
+
actions: ['ssm:GetParameter', 'ssm:GetParameters', 'ssm:PutParameter'],
|
|
101
|
+
resources: [
|
|
102
|
+
`arn:aws:ssm:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:parameter/prism/continuum/*`,
|
|
103
|
+
],
|
|
104
|
+
}),
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
NagSuppressions.addResourceSuppressions(
|
|
109
|
+
continuumCiPolicy,
|
|
110
|
+
[
|
|
111
|
+
{
|
|
112
|
+
id: 'AwsSolutions-IAM5',
|
|
113
|
+
reason: 'Agent space ID is dynamic; wildcard scoped to securityagent service resources only',
|
|
114
|
+
appliesTo: [`Resource::arn:aws:securityagent:<AWS::Region>:<AWS::AccountId>:agent-space/*`],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'AwsSolutions-IAM5',
|
|
118
|
+
reason: 'Scan bucket objects include dynamic diff filenames (SHA-based); wildcard on objects only',
|
|
119
|
+
appliesTo: ['Resource::<SecurityAgentScanBucketB752F4A9.Arn>/*'],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'AwsSolutions-IAM5',
|
|
123
|
+
reason: 'SSM parameters under /prism/continuum/ are all non-sensitive config values (IDs and bucket names)',
|
|
124
|
+
appliesTo: ['Resource::arn:aws:ssm:<AWS::Region>:<AWS::AccountId>:parameter/prism/continuum/*'],
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
true,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
new cdk.CfnOutput(scope, 'ContinuumCiPolicyArn', {
|
|
131
|
+
value: continuumCiPolicy.managedPolicyArn,
|
|
132
|
+
description: 'Attach this policy to your GitHub OIDC role for Continuum CI scans',
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return securityAgent;
|
|
136
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as cdk from 'aws-cdk-lib';
|
|
2
|
+
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
3
|
+
import * as kms from 'aws-cdk-lib/aws-kms';
|
|
4
|
+
import { Construct } from 'constructs';
|
|
5
|
+
import { NagSuppressions } from 'cdk-nag';
|
|
6
|
+
import { SecurityAgentConstruct } from './constructs/security-agent-construct';
|
|
7
|
+
import { addSecurityAgentResources } from './security-agent-resources';
|
|
8
|
+
|
|
9
|
+
export interface SecurityAgentStackProps extends cdk.StackProps {
|
|
10
|
+
/**
|
|
11
|
+
* ARN of an existing KMS key to encrypt the agent space with. Its key policy
|
|
12
|
+
* must already allow securityagent.amazonaws.com to encrypt and decrypt; an
|
|
13
|
+
* imported key's policy cannot be changed from here. Default: a new key.
|
|
14
|
+
*/
|
|
15
|
+
kmsKeyArn?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* PrismD1SecurityAgent: AWS Security Agent for PRISM without the metrics
|
|
20
|
+
* pipeline. Same resources, names and exports as the pipeline's
|
|
21
|
+
* enableSecurityAgent=true path, plus its own KMS key. Only for accounts that
|
|
22
|
+
* do not host Security Agent in PrismD1MetricsPipeline; the fixed resource
|
|
23
|
+
* and export names make CloudFormation refuse a second copy.
|
|
24
|
+
*
|
|
25
|
+
* Not here: the security-agent-processor and remediation tracker Lambdas,
|
|
26
|
+
* which read the pipeline's tables and stay in PrismD1MetricsPipeline.
|
|
27
|
+
*/
|
|
28
|
+
export class SecurityAgentStack extends cdk.Stack {
|
|
29
|
+
public readonly securityAgent: SecurityAgentConstruct;
|
|
30
|
+
|
|
31
|
+
constructor(scope: Construct, id: string, props: SecurityAgentStackProps = {}) {
|
|
32
|
+
super(scope, id, props);
|
|
33
|
+
|
|
34
|
+
let key: kms.IKey;
|
|
35
|
+
if (props.kmsKeyArn) {
|
|
36
|
+
key = kms.Key.fromKeyArn(this, 'SecurityAgentKey', props.kmsKeyArn);
|
|
37
|
+
} else {
|
|
38
|
+
// The same grants the pipeline's PrismDataKey gives Security Agent.
|
|
39
|
+
const k = new kms.Key(this, 'SecurityAgentKey', {
|
|
40
|
+
alias: 'alias/prism-d1-security-agent-key',
|
|
41
|
+
description: 'Encryption key for the PRISM D1 Security Agent space',
|
|
42
|
+
enableKeyRotation: true,
|
|
43
|
+
removalPolicy: cdk.RemovalPolicy.RETAIN,
|
|
44
|
+
});
|
|
45
|
+
k.grantEncryptDecrypt(new iam.ServicePrincipal('securityagent.amazonaws.com'));
|
|
46
|
+
k.addToResourcePolicy(new iam.PolicyStatement({
|
|
47
|
+
effect: iam.Effect.ALLOW,
|
|
48
|
+
principals: [new iam.ServicePrincipal('logs.amazonaws.com')],
|
|
49
|
+
actions: ['kms:Encrypt', 'kms:Decrypt', 'kms:GenerateDataKey*', 'kms:DescribeKey'],
|
|
50
|
+
resources: ['*'],
|
|
51
|
+
conditions: {
|
|
52
|
+
ArnLike: {
|
|
53
|
+
'kms:EncryptionContext:aws:logs:arn': `arn:aws:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/securityagent/*`,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
}));
|
|
57
|
+
key = k;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.securityAgent = addSecurityAgentResources(this, key);
|
|
61
|
+
|
|
62
|
+
// The pipeline stack carries the same suppression stack-wide.
|
|
63
|
+
NagSuppressions.addStackSuppressions(this, [{
|
|
64
|
+
id: 'AwsSolutions-IAM5',
|
|
65
|
+
reason: 'CDK KMS grant generates kms:GenerateDataKey* and kms:ReEncrypt* wildcard actions which are required for envelope encryption',
|
|
66
|
+
appliesTo: ['Action::kms:GenerateDataKey*', 'Action::kms:ReEncrypt*'],
|
|
67
|
+
}]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as cdk from 'aws-cdk-lib';
|
|
3
|
+
import { PentestTargetStack } from '../lib/pentest-target-stack';
|
|
4
|
+
|
|
5
|
+
const app = new cdk.App();
|
|
6
|
+
const slug = String(app.node.tryGetContext('slug') || '');
|
|
7
|
+
if (!slug) throw new Error('pentest-cdk: context "slug" is required (the adapter sets it)');
|
|
8
|
+
// The adapter addresses this stack by the same name (PrismPentest-<slug>).
|
|
9
|
+
new PentestTargetStack(app, `PrismPentest-${slug}`, {
|
|
10
|
+
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
|
|
11
|
+
description: 'prism-cli security pentest target: private VPC + Fargate + internal ALB (temporary)',
|
|
12
|
+
});
|