@jaypie/constructs 1.2.28 → 1.2.30

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.
@@ -7,7 +7,7 @@ export { JaypieCertificate, JaypieCertificateProps } from "./JaypieCertificate";
7
7
  export { JaypieDatadogBucket, JaypieDatadogBucketProps, } from "./JaypieDatadogBucket";
8
8
  export { JaypieDatadogForwarder, JaypieDatadogForwarderProps, } from "./JaypieDatadogForwarder";
9
9
  export { JaypieDatadogSecret } from "./JaypieDatadogSecret";
10
- export { JaypieDistribution, JaypieDistributionProps, } from "./JaypieDistribution";
10
+ export { JaypieDistribution, JaypieDistributionProps, SecurityHeadersOverrides, } from "./JaypieDistribution";
11
11
  export { JaypieDnsRecord, JaypieDnsRecordProps } from "./JaypieDnsRecord";
12
12
  export { JaypieDynamoDb, JaypieDynamoDbProps } from "./JaypieDynamoDb";
13
13
  export type { IndexDefinition } from "@jaypie/fabric";
@@ -7,6 +7,14 @@ import * as s3 from "aws-cdk-lib/aws-s3";
7
7
  import { LambdaDestination } from "aws-cdk-lib/aws-s3-notifications";
8
8
  import { Construct } from "constructs";
9
9
  import { HostConfig } from "./helpers";
10
+ export interface SecurityHeadersOverrides {
11
+ contentSecurityPolicy?: string;
12
+ frameOption?: cloudfront.HeadersFrameOption;
13
+ hstsIncludeSubdomains?: boolean;
14
+ hstsMaxAge?: number;
15
+ permissionsPolicy?: string;
16
+ referrerPolicy?: cloudfront.HeadersReferrerPolicy;
17
+ }
10
18
  export interface JaypieDistributionProps extends Omit<cloudfront.DistributionProps, "certificate" | "defaultBehavior" | "logBucket"> {
11
19
  /**
12
20
  * SSL certificate for the CloudFront distribution
@@ -73,6 +81,19 @@ export interface JaypieDistributionProps extends Omit<cloudfront.DistributionPro
73
81
  * @max Duration.seconds(120)
74
82
  */
75
83
  originReadTimeout?: Duration;
84
+ /**
85
+ * Full override for the response headers policy.
86
+ * When provided, bypasses all default security header logic.
87
+ */
88
+ responseHeadersPolicy?: cloudfront.IResponseHeadersPolicy;
89
+ /**
90
+ * Security headers configuration.
91
+ * - true/undefined: apply sensible defaults (HSTS, X-Frame-Options, CSP, etc.)
92
+ * - false: disable security headers entirely
93
+ * - SecurityHeadersOverrides object: merge overrides with defaults
94
+ * @default true
95
+ */
96
+ securityHeaders?: boolean | SecurityHeadersOverrides;
76
97
  /**
77
98
  * Role tag for tagging resources
78
99
  * @default CDK.ROLE.HOSTING
@@ -94,6 +115,7 @@ export declare class JaypieDistribution extends Construct implements cloudfront.
94
115
  readonly functionUrl?: lambda.FunctionUrl;
95
116
  readonly host?: string;
96
117
  readonly logBucket?: s3.IBucket;
118
+ readonly responseHeadersPolicy?: cloudfront.IResponseHeadersPolicy;
97
119
  constructor(scope: Construct, id: string, props: JaypieDistributionProps);
98
120
  private isIOrigin;
99
121
  private isIFunctionUrl;
@@ -8,19 +8,11 @@ export interface JaypieDynamoDbProps extends Omit<dynamodb.TablePropsV2, "global
8
8
  * - `undefined`: No GSIs (default)
9
9
  * - Array of IndexDefinition: Use the specified indexes
10
10
  *
11
- * Use `JaypieDynamoDb.DEFAULT_INDEXES` for the standard Jaypie GSIs.
12
- *
13
11
  * @example
14
12
  * // No GSIs (default)
15
13
  * new JaypieDynamoDb(this, "myTable");
16
14
  *
17
15
  * @example
18
- * // With default Jaypie indexes
19
- * new JaypieDynamoDb(this, "myTable", {
20
- * indexes: JaypieDynamoDb.DEFAULT_INDEXES,
21
- * });
22
- *
23
- * @example
24
16
  * // With custom indexes
25
17
  * new JaypieDynamoDb(this, "myTable", {
26
18
  * indexes: [
@@ -73,12 +65,6 @@ export interface JaypieDynamoDbProps extends Omit<dynamodb.TablePropsV2, "global
73
65
  * const table = new JaypieDynamoDb(this, "myApp");
74
66
  *
75
67
  * @example
76
- * // With default Jaypie indexes
77
- * const table = new JaypieDynamoDb(this, "myApp", {
78
- * indexes: JaypieDynamoDb.DEFAULT_INDEXES,
79
- * });
80
- *
81
- * @example
82
68
  * // With explicit table name (overrides CDK-generated name)
83
69
  * const table = new JaypieDynamoDb(this, "MyTable", {
84
70
  * tableName: "custom-table-name",
@@ -89,11 +75,6 @@ export interface JaypieDynamoDbProps extends Omit<dynamodb.TablePropsV2, "global
89
75
  * });
90
76
  */
91
77
  export declare class JaypieDynamoDb extends Construct implements dynamodb.ITableV2 {
92
- /**
93
- * Default Jaypie GSI definitions from @jaypie/fabric.
94
- * Pass to `indexes` prop to create all standard GSIs.
95
- */
96
- static readonly DEFAULT_INDEXES: IndexDefinition[];
97
78
  private readonly _table;
98
79
  constructor(scope: Construct, id: string, props?: JaypieDynamoDbProps);
99
80
  /**
@@ -98,6 +98,11 @@ export declare const CDK: {
98
98
  PROJECT: {
99
99
  INFRASTRUCTURE: string;
100
100
  };
101
+ SECURITY_HEADERS: {
102
+ CONTENT_SECURITY_POLICY: string;
103
+ HSTS_MAX_AGE: number;
104
+ PERMISSIONS_POLICY: string;
105
+ };
101
106
  ROLE: {
102
107
  API: string;
103
108
  DEPLOY: string;
@@ -7,7 +7,7 @@ export { JaypieCertificate, JaypieCertificateProps } from "./JaypieCertificate";
7
7
  export { JaypieDatadogBucket, JaypieDatadogBucketProps, } from "./JaypieDatadogBucket";
8
8
  export { JaypieDatadogForwarder, JaypieDatadogForwarderProps, } from "./JaypieDatadogForwarder";
9
9
  export { JaypieDatadogSecret } from "./JaypieDatadogSecret";
10
- export { JaypieDistribution, JaypieDistributionProps, } from "./JaypieDistribution";
10
+ export { JaypieDistribution, JaypieDistributionProps, SecurityHeadersOverrides, } from "./JaypieDistribution";
11
11
  export { JaypieDnsRecord, JaypieDnsRecordProps } from "./JaypieDnsRecord";
12
12
  export { JaypieDynamoDb, JaypieDynamoDbProps } from "./JaypieDynamoDb";
13
13
  export type { IndexDefinition } from "@jaypie/fabric";
package/dist/esm/index.js CHANGED
@@ -25,7 +25,7 @@ import { LambdaFunction } from 'aws-cdk-lib/aws-events-targets';
25
25
  import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
26
26
  import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
27
27
  import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
28
- import { DEFAULT_INDEXES, generateIndexName, DEFAULT_SORT_KEY } from '@jaypie/fabric';
28
+ import { generateIndexName, DEFAULT_SORT_KEY } from '@jaypie/fabric';
29
29
  import { Nextjs } from 'cdk-nextjs-standalone';
30
30
  import * as path from 'path';
31
31
  import { Trail, ReadWriteType } from 'aws-cdk-lib/aws-cloudtrail';
@@ -135,6 +135,11 @@ const CDK$2 = {
135
135
  PROJECT: {
136
136
  INFRASTRUCTURE: "infrastructure",
137
137
  },
138
+ SECURITY_HEADERS: {
139
+ CONTENT_SECURITY_POLICY: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self';",
140
+ HSTS_MAX_AGE: 63072000, // 2 years
141
+ PERMISSIONS_POLICY: "camera=(), microphone=(), geolocation=(), payment=()",
142
+ },
138
143
  ROLE: {
139
144
  API: "api",
140
145
  DEPLOY: "deploy",
@@ -2330,7 +2335,7 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
2330
2335
  class JaypieDistribution extends Construct {
2331
2336
  constructor(scope, id, props) {
2332
2337
  super(scope, id);
2333
- const { certificate: certificateProp = true, defaultBehavior: propsDefaultBehavior, destination: destinationProp = true, handler, host: propsHost, logBucket: logBucketProp, originReadTimeout = Duration.seconds(CDK$2.DURATION.CLOUDFRONT_API), roleTag = CDK$2.ROLE.API, streaming = false, zone: propsZone, ...distributionProps } = props;
2338
+ const { certificate: certificateProp = true, defaultBehavior: propsDefaultBehavior, destination: destinationProp = true, handler, host: propsHost, logBucket: logBucketProp, originReadTimeout = Duration.seconds(CDK$2.DURATION.CLOUDFRONT_API), responseHeadersPolicy: responseHeadersPolicyProp, roleTag = CDK$2.ROLE.API, securityHeaders: securityHeadersProp, streaming = false, zone: propsZone, ...distributionProps } = props;
2334
2339
  // Validate environment variables
2335
2340
  if (process.env.CDK_ENV_API_SUBDOMAIN &&
2336
2341
  !isValidSubdomain(process.env.CDK_ENV_API_SUBDOMAIN)) {
@@ -2412,6 +2417,62 @@ class JaypieDistribution extends Construct {
2412
2417
  handler.addEnvironment("PROJECT_BASE_URL", `https://${host}`);
2413
2418
  }
2414
2419
  }
2420
+ // Resolve response headers policy for security headers
2421
+ let resolvedResponseHeadersPolicy;
2422
+ if (responseHeadersPolicyProp) {
2423
+ resolvedResponseHeadersPolicy = responseHeadersPolicyProp;
2424
+ }
2425
+ else if (securityHeadersProp !== false) {
2426
+ const overrides = typeof securityHeadersProp === "object" ? securityHeadersProp : {};
2427
+ resolvedResponseHeadersPolicy = new cloudfront.ResponseHeadersPolicy(this, "SecurityHeaders", {
2428
+ customHeadersBehavior: {
2429
+ customHeaders: [
2430
+ {
2431
+ header: "Cross-Origin-Opener-Policy",
2432
+ override: true,
2433
+ value: "same-origin",
2434
+ },
2435
+ {
2436
+ header: "Cross-Origin-Resource-Policy",
2437
+ override: true,
2438
+ value: "same-origin",
2439
+ },
2440
+ {
2441
+ header: "Permissions-Policy",
2442
+ override: true,
2443
+ value: overrides.permissionsPolicy ??
2444
+ CDK$2.SECURITY_HEADERS.PERMISSIONS_POLICY,
2445
+ },
2446
+ ],
2447
+ },
2448
+ removeHeaders: ["Server"],
2449
+ securityHeadersBehavior: {
2450
+ contentSecurityPolicy: {
2451
+ contentSecurityPolicy: overrides.contentSecurityPolicy ??
2452
+ CDK$2.SECURITY_HEADERS.CONTENT_SECURITY_POLICY,
2453
+ override: true,
2454
+ },
2455
+ contentTypeOptions: { override: true },
2456
+ frameOptions: {
2457
+ frameOption: overrides.frameOption ?? cloudfront.HeadersFrameOption.DENY,
2458
+ override: true,
2459
+ },
2460
+ referrerPolicy: {
2461
+ referrerPolicy: overrides.referrerPolicy ??
2462
+ cloudfront.HeadersReferrerPolicy
2463
+ .STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
2464
+ override: true,
2465
+ },
2466
+ strictTransportSecurity: {
2467
+ accessControlMaxAge: Duration.seconds(overrides.hstsMaxAge ?? CDK$2.SECURITY_HEADERS.HSTS_MAX_AGE),
2468
+ includeSubdomains: overrides.hstsIncludeSubdomains ?? true,
2469
+ override: true,
2470
+ preload: true,
2471
+ },
2472
+ },
2473
+ });
2474
+ }
2475
+ this.responseHeadersPolicy = resolvedResponseHeadersPolicy;
2415
2476
  // Build default behavior
2416
2477
  let defaultBehavior;
2417
2478
  if (propsDefaultBehavior) {
@@ -2423,6 +2484,9 @@ class JaypieDistribution extends Construct {
2423
2484
  cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
2424
2485
  origin,
2425
2486
  originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
2487
+ ...(resolvedResponseHeadersPolicy
2488
+ ? { responseHeadersPolicy: resolvedResponseHeadersPolicy }
2489
+ : {}),
2426
2490
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
2427
2491
  };
2428
2492
  }
@@ -2730,12 +2794,6 @@ function indexesToGsi(indexes) {
2730
2794
  * const table = new JaypieDynamoDb(this, "myApp");
2731
2795
  *
2732
2796
  * @example
2733
- * // With default Jaypie indexes
2734
- * const table = new JaypieDynamoDb(this, "myApp", {
2735
- * indexes: JaypieDynamoDb.DEFAULT_INDEXES,
2736
- * });
2737
- *
2738
- * @example
2739
2797
  * // With explicit table name (overrides CDK-generated name)
2740
2798
  * const table = new JaypieDynamoDb(this, "MyTable", {
2741
2799
  * tableName: "custom-table-name",
@@ -2874,11 +2932,6 @@ class JaypieDynamoDb extends Construct {
2874
2932
  return this._table.metricUserErrors(props);
2875
2933
  }
2876
2934
  }
2877
- /**
2878
- * Default Jaypie GSI definitions from @jaypie/fabric.
2879
- * Pass to `indexes` prop to create all standard GSIs.
2880
- */
2881
- JaypieDynamoDb.DEFAULT_INDEXES = DEFAULT_INDEXES;
2882
2935
 
2883
2936
  class JaypieEventsRule extends Construct {
2884
2937
  /**
@@ -3661,6 +3714,7 @@ class JaypieSsoPermissions extends Construct {
3661
3714
  "servicecatalog:*",
3662
3715
  "sns:*",
3663
3716
  "sqs:*",
3717
+ "ssm:*",
3664
3718
  "states:*",
3665
3719
  "tag:*",
3666
3720
  "uxc:*",
@@ -3888,8 +3942,9 @@ class JaypieWebDeploymentBucket extends Construct {
3888
3942
  if (process.env.CDK_ENV_REPO) {
3889
3943
  repo = `repo:${process.env.CDK_ENV_REPO}:*`;
3890
3944
  }
3945
+ let bucketDeployRole;
3891
3946
  if (repo) {
3892
- const bucketDeployRole = new Role(this, "DestinationBucketDeployRole", {
3947
+ bucketDeployRole = new Role(this, "DestinationBucketDeployRole", {
3893
3948
  assumedBy: new FederatedPrincipal(Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), {
3894
3949
  StringLike: {
3895
3950
  "token.actions.githubusercontent.com:sub": repo,
@@ -3989,6 +4044,16 @@ class JaypieWebDeploymentBucket extends Construct {
3989
4044
  new CfnOutput(this, "DistributionId", {
3990
4045
  value: this.distribution.distributionId,
3991
4046
  });
4047
+ // Add CloudFront invalidation permission to deploy role if it exists
4048
+ if (bucketDeployRole) {
4049
+ bucketDeployRole.addToPolicy(new PolicyStatement({
4050
+ effect: Effect.ALLOW,
4051
+ actions: ["cloudfront:CreateInvalidation"],
4052
+ resources: [
4053
+ `arn:aws:cloudfront::${Stack.of(this).account}:distribution/${this.distribution.distributionId}`,
4054
+ ],
4055
+ }));
4056
+ }
3992
4057
  }
3993
4058
  }
3994
4059
  // Implement remaining IBucket methods by delegating to the bucket