@jaypie/constructs 1.2.54 → 1.2.55

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.
@@ -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";
@@ -16,6 +17,8 @@ export interface JaypieMigrationProps {
16
17
  secrets?: SecretsArrayItem[];
17
18
  /** DynamoDB tables to grant read/write access */
18
19
  tables?: dynamodb.ITable[];
20
+ /** Lambda timeout. Defaults to 15 minutes (Lambda max). */
21
+ timeout?: cdk.Duration;
19
22
  }
20
23
  export declare class JaypieMigration extends Construct {
21
24
  readonly lambda: JaypieLambda;
@@ -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 ruleActionOverrides = managedRuleOverrides?.[ruleName];
2726
- const scopeDownStatement = managedRuleScopeDowns?.[ruleName];
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
- ...(ruleActionOverrides && { ruleActionOverrides }),
2736
- ...(scopeDownStatement && { scopeDownStatement }),
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", secrets = [], tables = [], timeout = cdk__namespace.Duration.minutes(15), } = 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: cdk__namespace.Duration.minutes(5),
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