@jaypie/constructs 1.2.54 → 1.2.56
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/cjs/JaypieDistribution.d.ts +31 -0
- package/dist/cjs/JaypieMigration.d.ts +7 -0
- package/dist/cjs/index.cjs +113 -12
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/JaypieDistribution.d.ts +31 -0
- package/dist/esm/JaypieMigration.d.ts +7 -0
- package/dist/esm/index.js +113 -12
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -8,6 +8,17 @@ import { LambdaDestination } from "aws-cdk-lib/aws-s3-notifications";
|
|
|
8
8
|
import * as wafv2 from "aws-cdk-lib/aws-wafv2";
|
|
9
9
|
import { Construct } from "constructs";
|
|
10
10
|
import { HostConfig } from "./helpers";
|
|
11
|
+
/**
|
|
12
|
+
* One entry in a `waf.allow` list. Names one or more URL paths and, for each
|
|
13
|
+
* managed rule group key, the sub-rule names to flip from `block` to `count`
|
|
14
|
+
* on that path set. See JaypieWafConfig.allow.
|
|
15
|
+
*/
|
|
16
|
+
export interface JaypieWafAllowEntry {
|
|
17
|
+
/** URL path or paths. Trailing `*` → STARTS_WITH; otherwise EXACTLY. */
|
|
18
|
+
path: string | string[];
|
|
19
|
+
/** Managed-rule-group keys (e.g. AWSManagedRulesCommonRuleSet) → sub-rule names. */
|
|
20
|
+
[ruleGroupKey: string]: string | string[] | undefined;
|
|
21
|
+
}
|
|
11
22
|
export interface JaypieWafConfig {
|
|
12
23
|
/**
|
|
13
24
|
* Unique name for this distribution's WAF resources. Required when passing a
|
|
@@ -76,6 +87,26 @@ export interface JaypieWafConfig {
|
|
|
76
87
|
* @default 2000
|
|
77
88
|
*/
|
|
78
89
|
rateLimitPerIp?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Path-scoped relaxations layered on top of the default managed-rule groups.
|
|
92
|
+
* Each entry names one or more URL paths and, for each managed rule group
|
|
93
|
+
* key, the sub-rule names to flip from `block` to `count` on that path set.
|
|
94
|
+
* Strict default action is preserved on every other path.
|
|
95
|
+
*
|
|
96
|
+
* Composes with `managedRuleOverrides`: the baseline override list applies
|
|
97
|
+
* to both the relaxed and strict emissions of a group; entries in `allow`
|
|
98
|
+
* additionally relax specific (path × sub-rule) intersections.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* allow: [
|
|
102
|
+
* {
|
|
103
|
+
* path: "/hooks/*",
|
|
104
|
+
* AWSManagedRulesCommonRuleSet: ["ExploitablePaths_URIPATH"],
|
|
105
|
+
* AWSManagedRulesKnownBadInputsRuleSet: ["CrossSiteScripting_BODY"],
|
|
106
|
+
* },
|
|
107
|
+
* ]
|
|
108
|
+
*/
|
|
109
|
+
allow?: JaypieWafAllowEntry | JaypieWafAllowEntry[];
|
|
79
110
|
/**
|
|
80
111
|
* Use an existing WebACL ARN instead of creating one
|
|
81
112
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
|
+
import * as cdk from "aws-cdk-lib";
|
|
2
3
|
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
|
|
3
4
|
import * as lambda from "aws-cdk-lib/aws-lambda";
|
|
4
5
|
import { JaypieLambda } from "./JaypieLambda";
|
|
@@ -12,10 +13,16 @@ export interface JaypieMigrationProps {
|
|
|
12
13
|
environment?: Record<string, string> | (Record<string, string> | string)[];
|
|
13
14
|
/** Lambda handler entry point */
|
|
14
15
|
handler?: string;
|
|
16
|
+
/** Polling interval between isCompleteHandler invocations. Default: 60 seconds. */
|
|
17
|
+
queryInterval?: cdk.Duration;
|
|
15
18
|
/** Secrets to make available to the migration Lambda */
|
|
16
19
|
secrets?: SecretsArrayItem[];
|
|
17
20
|
/** DynamoDB tables to grant read/write access */
|
|
18
21
|
tables?: dynamodb.ITable[];
|
|
22
|
+
/** Lambda timeout per invocation. Defaults to 15 minutes (Lambda max). */
|
|
23
|
+
timeout?: cdk.Duration;
|
|
24
|
+
/** Maximum total wall time across all isCompleteHandler invocations. Default: 2 hours. */
|
|
25
|
+
totalTimeout?: cdk.Duration;
|
|
19
26
|
}
|
|
20
27
|
export declare class JaypieMigration extends Construct {
|
|
21
28
|
readonly lambda: JaypieLambda;
|
package/dist/esm/index.js
CHANGED
|
@@ -950,7 +950,7 @@ function checkEnvIsProvider$1(env = process.env) {
|
|
|
950
950
|
function cleanName$1(name) {
|
|
951
951
|
return name.replace(/[^a-zA-Z0-9:-]/g, "");
|
|
952
952
|
}
|
|
953
|
-
function exportEnvName$1(name, env = process.env) {
|
|
953
|
+
function exportEnvName$1(name, env = process.env, consumer = false) {
|
|
954
954
|
let rawName;
|
|
955
955
|
if (checkEnvIsProvider$1(env)) {
|
|
956
956
|
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
@@ -958,7 +958,7 @@ function exportEnvName$1(name, env = process.env) {
|
|
|
958
958
|
return cleanName$1(rawName);
|
|
959
959
|
}
|
|
960
960
|
else {
|
|
961
|
-
if (checkEnvIsConsumer$1(env)) {
|
|
961
|
+
if (consumer || checkEnvIsConsumer$1(env)) {
|
|
962
962
|
rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
|
|
963
963
|
}
|
|
964
964
|
else {
|
|
@@ -986,7 +986,7 @@ class JaypieEnvSecret extends Construct {
|
|
|
986
986
|
this._envKey = envKey;
|
|
987
987
|
let exportName;
|
|
988
988
|
if (!exportParam) {
|
|
989
|
-
exportName = exportEnvName$1(id);
|
|
989
|
+
exportName = exportEnvName$1(envKey || id, process.env, consumer);
|
|
990
990
|
}
|
|
991
991
|
else {
|
|
992
992
|
exportName = cleanName$1(exportParam);
|
|
@@ -2681,13 +2681,110 @@ class JaypieDistribution extends Construct {
|
|
|
2681
2681
|
}
|
|
2682
2682
|
else {
|
|
2683
2683
|
// Create new WebACL
|
|
2684
|
-
const { managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES$1, rateLimitPerIp = DEFAULT_RATE_LIMIT$1, } = wafConfig;
|
|
2684
|
+
const { allow, managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES$1, rateLimitPerIp = DEFAULT_RATE_LIMIT$1, } = wafConfig;
|
|
2685
|
+
const allowEntries = allow
|
|
2686
|
+
? Array.isArray(allow)
|
|
2687
|
+
? allow
|
|
2688
|
+
: [allow]
|
|
2689
|
+
: [];
|
|
2690
|
+
const groupAllowances = {};
|
|
2691
|
+
for (const entry of allowEntries) {
|
|
2692
|
+
const paths = Array.isArray(entry.path) ? entry.path : [entry.path];
|
|
2693
|
+
for (const key of Object.keys(entry)) {
|
|
2694
|
+
if (key === "path")
|
|
2695
|
+
continue;
|
|
2696
|
+
const raw = entry[key];
|
|
2697
|
+
if (raw == null)
|
|
2698
|
+
continue;
|
|
2699
|
+
const ruleNames = Array.isArray(raw) ? raw : [raw];
|
|
2700
|
+
if (!groupAllowances[key])
|
|
2701
|
+
groupAllowances[key] = [];
|
|
2702
|
+
groupAllowances[key].push({ paths, ruleNames });
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
const pathToStatement = (path) => {
|
|
2706
|
+
const isPrefix = path.endsWith("*");
|
|
2707
|
+
return {
|
|
2708
|
+
byteMatchStatement: {
|
|
2709
|
+
fieldToMatch: { uriPath: {} },
|
|
2710
|
+
positionalConstraint: isPrefix ? "STARTS_WITH" : "EXACTLY",
|
|
2711
|
+
searchString: isPrefix ? path.slice(0, -1) : path,
|
|
2712
|
+
textTransformations: [{ priority: 0, type: "NONE" }],
|
|
2713
|
+
},
|
|
2714
|
+
};
|
|
2715
|
+
};
|
|
2716
|
+
const pathsToStatement = (paths) => {
|
|
2717
|
+
if (paths.length === 1)
|
|
2718
|
+
return pathToStatement(paths[0]);
|
|
2719
|
+
return {
|
|
2720
|
+
orStatement: { statements: paths.map(pathToStatement) },
|
|
2721
|
+
};
|
|
2722
|
+
};
|
|
2723
|
+
const andScopeDown = (base, extra) => base ? { andStatement: { statements: [base, extra] } } : extra;
|
|
2685
2724
|
let priority = 0;
|
|
2686
2725
|
const rules = [];
|
|
2687
2726
|
// Add managed rule groups
|
|
2688
2727
|
for (const ruleName of managedRules) {
|
|
2689
|
-
const
|
|
2690
|
-
const
|
|
2728
|
+
const baseOverrides = managedRuleOverrides?.[ruleName];
|
|
2729
|
+
const baseScopeDown = managedRuleScopeDowns?.[ruleName];
|
|
2730
|
+
const allowances = groupAllowances[ruleName];
|
|
2731
|
+
if (!allowances || allowances.length === 0) {
|
|
2732
|
+
rules.push({
|
|
2733
|
+
name: ruleName,
|
|
2734
|
+
priority: priority++,
|
|
2735
|
+
overrideAction: { none: {} },
|
|
2736
|
+
statement: {
|
|
2737
|
+
managedRuleGroupStatement: {
|
|
2738
|
+
name: ruleName,
|
|
2739
|
+
vendorName: "AWS",
|
|
2740
|
+
...(baseOverrides && { ruleActionOverrides: baseOverrides }),
|
|
2741
|
+
...(baseScopeDown && { scopeDownStatement: baseScopeDown }),
|
|
2742
|
+
},
|
|
2743
|
+
},
|
|
2744
|
+
visibilityConfig: {
|
|
2745
|
+
cloudWatchMetricsEnabled: true,
|
|
2746
|
+
metricName: ruleName,
|
|
2747
|
+
sampledRequestsEnabled: true,
|
|
2748
|
+
},
|
|
2749
|
+
});
|
|
2750
|
+
continue;
|
|
2751
|
+
}
|
|
2752
|
+
// Emit one relaxed rule per allow entry that names this group
|
|
2753
|
+
allowances.forEach(({ paths, ruleNames }, index) => {
|
|
2754
|
+
const entryOverrides = ruleNames.map((n) => ({
|
|
2755
|
+
name: n,
|
|
2756
|
+
actionToUse: { count: {} },
|
|
2757
|
+
}));
|
|
2758
|
+
const combinedOverrides = [
|
|
2759
|
+
...(baseOverrides ?? []),
|
|
2760
|
+
...entryOverrides,
|
|
2761
|
+
];
|
|
2762
|
+
const relaxedScopeDown = andScopeDown(baseScopeDown, pathsToStatement(paths));
|
|
2763
|
+
const relaxedName = `${ruleName}-allow-${index}`;
|
|
2764
|
+
rules.push({
|
|
2765
|
+
name: relaxedName,
|
|
2766
|
+
priority: priority++,
|
|
2767
|
+
overrideAction: { none: {} },
|
|
2768
|
+
statement: {
|
|
2769
|
+
managedRuleGroupStatement: {
|
|
2770
|
+
name: ruleName,
|
|
2771
|
+
vendorName: "AWS",
|
|
2772
|
+
ruleActionOverrides: combinedOverrides,
|
|
2773
|
+
scopeDownStatement: relaxedScopeDown,
|
|
2774
|
+
},
|
|
2775
|
+
},
|
|
2776
|
+
visibilityConfig: {
|
|
2777
|
+
cloudWatchMetricsEnabled: true,
|
|
2778
|
+
metricName: relaxedName,
|
|
2779
|
+
sampledRequestsEnabled: true,
|
|
2780
|
+
},
|
|
2781
|
+
});
|
|
2782
|
+
});
|
|
2783
|
+
// Emit one strict rule whose scope-down excludes every relaxed path
|
|
2784
|
+
const allPaths = allowances.flatMap((a) => a.paths);
|
|
2785
|
+
const strictScopeDown = andScopeDown(baseScopeDown, {
|
|
2786
|
+
notStatement: { statement: pathsToStatement(allPaths) },
|
|
2787
|
+
});
|
|
2691
2788
|
rules.push({
|
|
2692
2789
|
name: ruleName,
|
|
2693
2790
|
priority: priority++,
|
|
@@ -2696,8 +2793,8 @@ class JaypieDistribution extends Construct {
|
|
|
2696
2793
|
managedRuleGroupStatement: {
|
|
2697
2794
|
name: ruleName,
|
|
2698
2795
|
vendorName: "AWS",
|
|
2699
|
-
...(
|
|
2700
|
-
|
|
2796
|
+
...(baseOverrides && { ruleActionOverrides: baseOverrides }),
|
|
2797
|
+
scopeDownStatement: strictScopeDown,
|
|
2701
2798
|
},
|
|
2702
2799
|
},
|
|
2703
2800
|
visibilityConfig: {
|
|
@@ -3536,8 +3633,7 @@ const DYNAMODB_CONTROL_PLANE_ACTIONS = [
|
|
|
3536
3633
|
class JaypieMigration extends Construct {
|
|
3537
3634
|
constructor(scope, id, props) {
|
|
3538
3635
|
super(scope, id);
|
|
3539
|
-
const { code, dependencies = [], environment, handler = "index.handler", secrets = [], tables = [], } = props;
|
|
3540
|
-
// Migration Lambda — 5 minute timeout for long-running migrations
|
|
3636
|
+
const { code, dependencies = [], environment, handler = "index.handler", queryInterval = cdk.Duration.seconds(60), secrets = [], tables = [], timeout = cdk.Duration.minutes(15), totalTimeout = cdk.Duration.hours(2), } = props;
|
|
3541
3637
|
this.lambda = new JaypieLambda(this, "MigrationLambda", {
|
|
3542
3638
|
code,
|
|
3543
3639
|
description: "DynamoDB migration custom resource",
|
|
@@ -3546,7 +3642,7 @@ class JaypieMigration extends Construct {
|
|
|
3546
3642
|
roleTag: CDK$2.ROLE.PROCESSING,
|
|
3547
3643
|
secrets,
|
|
3548
3644
|
tables,
|
|
3549
|
-
timeout
|
|
3645
|
+
timeout,
|
|
3550
3646
|
});
|
|
3551
3647
|
// Grant control-plane perms on the passed tables so migrations that
|
|
3552
3648
|
// alter table shape (GSIs, TTL, streams, backups) succeed. JaypieLambda
|
|
@@ -3560,9 +3656,14 @@ class JaypieMigration extends Construct {
|
|
|
3560
3656
|
]),
|
|
3561
3657
|
}));
|
|
3562
3658
|
}
|
|
3563
|
-
//
|
|
3659
|
+
// cr.Provider with isCompleteHandler enables the waiter pattern: onEventHandler
|
|
3660
|
+
// returns PhysicalResourceId immediately; isCompleteHandler is polled via Step
|
|
3661
|
+
// Functions until migrationHandler returns pending: false (or omits pending).
|
|
3564
3662
|
const provider = new cr.Provider(this, "MigrationProvider", {
|
|
3663
|
+
isCompleteHandler: this.lambda,
|
|
3565
3664
|
onEventHandler: this.lambda,
|
|
3665
|
+
queryInterval,
|
|
3666
|
+
totalTimeout,
|
|
3566
3667
|
});
|
|
3567
3668
|
// Custom Resource that triggers on every deploy.
|
|
3568
3669
|
// deployNonce forces CloudFormation to re-invoke the custom resource
|