@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/cjs/index.cjs
CHANGED
|
@@ -986,7 +986,7 @@ function checkEnvIsProvider$1(env = process.env) {
|
|
|
986
986
|
function cleanName$1(name) {
|
|
987
987
|
return name.replace(/[^a-zA-Z0-9:-]/g, "");
|
|
988
988
|
}
|
|
989
|
-
function exportEnvName$1(name, env = process.env) {
|
|
989
|
+
function exportEnvName$1(name, env = process.env, consumer = false) {
|
|
990
990
|
let rawName;
|
|
991
991
|
if (checkEnvIsProvider$1(env)) {
|
|
992
992
|
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
@@ -994,7 +994,7 @@ function exportEnvName$1(name, env = process.env) {
|
|
|
994
994
|
return cleanName$1(rawName);
|
|
995
995
|
}
|
|
996
996
|
else {
|
|
997
|
-
if (checkEnvIsConsumer$1(env)) {
|
|
997
|
+
if (consumer || checkEnvIsConsumer$1(env)) {
|
|
998
998
|
rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
|
|
999
999
|
}
|
|
1000
1000
|
else {
|
|
@@ -1022,7 +1022,7 @@ class JaypieEnvSecret extends constructs.Construct {
|
|
|
1022
1022
|
this._envKey = envKey;
|
|
1023
1023
|
let exportName;
|
|
1024
1024
|
if (!exportParam) {
|
|
1025
|
-
exportName = exportEnvName$1(id);
|
|
1025
|
+
exportName = exportEnvName$1(envKey || id, process.env, consumer);
|
|
1026
1026
|
}
|
|
1027
1027
|
else {
|
|
1028
1028
|
exportName = cleanName$1(exportParam);
|
|
@@ -2717,13 +2717,110 @@ class JaypieDistribution extends constructs.Construct {
|
|
|
2717
2717
|
}
|
|
2718
2718
|
else {
|
|
2719
2719
|
// Create new WebACL
|
|
2720
|
-
const { managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES$1, rateLimitPerIp = DEFAULT_RATE_LIMIT$1, } = wafConfig;
|
|
2720
|
+
const { allow, managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES$1, rateLimitPerIp = DEFAULT_RATE_LIMIT$1, } = wafConfig;
|
|
2721
|
+
const allowEntries = allow
|
|
2722
|
+
? Array.isArray(allow)
|
|
2723
|
+
? allow
|
|
2724
|
+
: [allow]
|
|
2725
|
+
: [];
|
|
2726
|
+
const groupAllowances = {};
|
|
2727
|
+
for (const entry of allowEntries) {
|
|
2728
|
+
const paths = Array.isArray(entry.path) ? entry.path : [entry.path];
|
|
2729
|
+
for (const key of Object.keys(entry)) {
|
|
2730
|
+
if (key === "path")
|
|
2731
|
+
continue;
|
|
2732
|
+
const raw = entry[key];
|
|
2733
|
+
if (raw == null)
|
|
2734
|
+
continue;
|
|
2735
|
+
const ruleNames = Array.isArray(raw) ? raw : [raw];
|
|
2736
|
+
if (!groupAllowances[key])
|
|
2737
|
+
groupAllowances[key] = [];
|
|
2738
|
+
groupAllowances[key].push({ paths, ruleNames });
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
const pathToStatement = (path) => {
|
|
2742
|
+
const isPrefix = path.endsWith("*");
|
|
2743
|
+
return {
|
|
2744
|
+
byteMatchStatement: {
|
|
2745
|
+
fieldToMatch: { uriPath: {} },
|
|
2746
|
+
positionalConstraint: isPrefix ? "STARTS_WITH" : "EXACTLY",
|
|
2747
|
+
searchString: isPrefix ? path.slice(0, -1) : path,
|
|
2748
|
+
textTransformations: [{ priority: 0, type: "NONE" }],
|
|
2749
|
+
},
|
|
2750
|
+
};
|
|
2751
|
+
};
|
|
2752
|
+
const pathsToStatement = (paths) => {
|
|
2753
|
+
if (paths.length === 1)
|
|
2754
|
+
return pathToStatement(paths[0]);
|
|
2755
|
+
return {
|
|
2756
|
+
orStatement: { statements: paths.map(pathToStatement) },
|
|
2757
|
+
};
|
|
2758
|
+
};
|
|
2759
|
+
const andScopeDown = (base, extra) => base ? { andStatement: { statements: [base, extra] } } : extra;
|
|
2721
2760
|
let priority = 0;
|
|
2722
2761
|
const rules = [];
|
|
2723
2762
|
// Add managed rule groups
|
|
2724
2763
|
for (const ruleName of managedRules) {
|
|
2725
|
-
const
|
|
2726
|
-
const
|
|
2764
|
+
const baseOverrides = managedRuleOverrides?.[ruleName];
|
|
2765
|
+
const baseScopeDown = managedRuleScopeDowns?.[ruleName];
|
|
2766
|
+
const allowances = groupAllowances[ruleName];
|
|
2767
|
+
if (!allowances || allowances.length === 0) {
|
|
2768
|
+
rules.push({
|
|
2769
|
+
name: ruleName,
|
|
2770
|
+
priority: priority++,
|
|
2771
|
+
overrideAction: { none: {} },
|
|
2772
|
+
statement: {
|
|
2773
|
+
managedRuleGroupStatement: {
|
|
2774
|
+
name: ruleName,
|
|
2775
|
+
vendorName: "AWS",
|
|
2776
|
+
...(baseOverrides && { ruleActionOverrides: baseOverrides }),
|
|
2777
|
+
...(baseScopeDown && { scopeDownStatement: baseScopeDown }),
|
|
2778
|
+
},
|
|
2779
|
+
},
|
|
2780
|
+
visibilityConfig: {
|
|
2781
|
+
cloudWatchMetricsEnabled: true,
|
|
2782
|
+
metricName: ruleName,
|
|
2783
|
+
sampledRequestsEnabled: true,
|
|
2784
|
+
},
|
|
2785
|
+
});
|
|
2786
|
+
continue;
|
|
2787
|
+
}
|
|
2788
|
+
// Emit one relaxed rule per allow entry that names this group
|
|
2789
|
+
allowances.forEach(({ paths, ruleNames }, index) => {
|
|
2790
|
+
const entryOverrides = ruleNames.map((n) => ({
|
|
2791
|
+
name: n,
|
|
2792
|
+
actionToUse: { count: {} },
|
|
2793
|
+
}));
|
|
2794
|
+
const combinedOverrides = [
|
|
2795
|
+
...(baseOverrides ?? []),
|
|
2796
|
+
...entryOverrides,
|
|
2797
|
+
];
|
|
2798
|
+
const relaxedScopeDown = andScopeDown(baseScopeDown, pathsToStatement(paths));
|
|
2799
|
+
const relaxedName = `${ruleName}-allow-${index}`;
|
|
2800
|
+
rules.push({
|
|
2801
|
+
name: relaxedName,
|
|
2802
|
+
priority: priority++,
|
|
2803
|
+
overrideAction: { none: {} },
|
|
2804
|
+
statement: {
|
|
2805
|
+
managedRuleGroupStatement: {
|
|
2806
|
+
name: ruleName,
|
|
2807
|
+
vendorName: "AWS",
|
|
2808
|
+
ruleActionOverrides: combinedOverrides,
|
|
2809
|
+
scopeDownStatement: relaxedScopeDown,
|
|
2810
|
+
},
|
|
2811
|
+
},
|
|
2812
|
+
visibilityConfig: {
|
|
2813
|
+
cloudWatchMetricsEnabled: true,
|
|
2814
|
+
metricName: relaxedName,
|
|
2815
|
+
sampledRequestsEnabled: true,
|
|
2816
|
+
},
|
|
2817
|
+
});
|
|
2818
|
+
});
|
|
2819
|
+
// Emit one strict rule whose scope-down excludes every relaxed path
|
|
2820
|
+
const allPaths = allowances.flatMap((a) => a.paths);
|
|
2821
|
+
const strictScopeDown = andScopeDown(baseScopeDown, {
|
|
2822
|
+
notStatement: { statement: pathsToStatement(allPaths) },
|
|
2823
|
+
});
|
|
2727
2824
|
rules.push({
|
|
2728
2825
|
name: ruleName,
|
|
2729
2826
|
priority: priority++,
|
|
@@ -2732,8 +2829,8 @@ class JaypieDistribution extends constructs.Construct {
|
|
|
2732
2829
|
managedRuleGroupStatement: {
|
|
2733
2830
|
name: ruleName,
|
|
2734
2831
|
vendorName: "AWS",
|
|
2735
|
-
...(
|
|
2736
|
-
|
|
2832
|
+
...(baseOverrides && { ruleActionOverrides: baseOverrides }),
|
|
2833
|
+
scopeDownStatement: strictScopeDown,
|
|
2737
2834
|
},
|
|
2738
2835
|
},
|
|
2739
2836
|
visibilityConfig: {
|
|
@@ -3572,8 +3669,7 @@ const DYNAMODB_CONTROL_PLANE_ACTIONS = [
|
|
|
3572
3669
|
class JaypieMigration extends constructs.Construct {
|
|
3573
3670
|
constructor(scope, id, props) {
|
|
3574
3671
|
super(scope, id);
|
|
3575
|
-
const { code, dependencies = [], environment, handler = "index.handler", secrets = [], tables = [], } = props;
|
|
3576
|
-
// Migration Lambda — 5 minute timeout for long-running migrations
|
|
3672
|
+
const { code, dependencies = [], environment, handler = "index.handler", queryInterval = cdk__namespace.Duration.seconds(60), secrets = [], tables = [], timeout = cdk__namespace.Duration.minutes(15), totalTimeout = cdk__namespace.Duration.hours(2), } = props;
|
|
3577
3673
|
this.lambda = new JaypieLambda(this, "MigrationLambda", {
|
|
3578
3674
|
code,
|
|
3579
3675
|
description: "DynamoDB migration custom resource",
|
|
@@ -3582,7 +3678,7 @@ class JaypieMigration extends constructs.Construct {
|
|
|
3582
3678
|
roleTag: CDK$2.ROLE.PROCESSING,
|
|
3583
3679
|
secrets,
|
|
3584
3680
|
tables,
|
|
3585
|
-
timeout
|
|
3681
|
+
timeout,
|
|
3586
3682
|
});
|
|
3587
3683
|
// Grant control-plane perms on the passed tables so migrations that
|
|
3588
3684
|
// alter table shape (GSIs, TTL, streams, backups) succeed. JaypieLambda
|
|
@@ -3596,9 +3692,14 @@ class JaypieMigration extends constructs.Construct {
|
|
|
3596
3692
|
]),
|
|
3597
3693
|
}));
|
|
3598
3694
|
}
|
|
3599
|
-
//
|
|
3695
|
+
// cr.Provider with isCompleteHandler enables the waiter pattern: onEventHandler
|
|
3696
|
+
// returns PhysicalResourceId immediately; isCompleteHandler is polled via Step
|
|
3697
|
+
// Functions until migrationHandler returns pending: false (or omits pending).
|
|
3600
3698
|
const provider = new cr__namespace.Provider(this, "MigrationProvider", {
|
|
3699
|
+
isCompleteHandler: this.lambda,
|
|
3601
3700
|
onEventHandler: this.lambda,
|
|
3701
|
+
queryInterval,
|
|
3702
|
+
totalTimeout,
|
|
3602
3703
|
});
|
|
3603
3704
|
// Custom Resource that triggers on every deploy.
|
|
3604
3705
|
// deployNonce forces CloudFormation to re-invoke the custom resource
|