@jaypie/constructs 1.2.59 → 1.2.61

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.
@@ -19,3 +19,4 @@ export { resolveEnvironment, EnvironmentArrayItem, EnvironmentInput, } from "./r
19
19
  export { resolveHostedZone } from "./resolveHostedZone";
20
20
  export { resolveParamsAndSecrets } from "./resolveParamsAndSecrets";
21
21
  export { resolveSecrets, SecretsArrayItem, clearSecretsCache, clearAllSecretsCaches, } from "./resolveSecrets";
22
+ export { assertValidWafRuleNames, AWS_MANAGED_RULE_GROUPS, } from "./wafManagedRuleNames";
@@ -2,6 +2,7 @@ export interface JaypieLambdaEnvOptions {
2
2
  initialEnvironment?: {
3
3
  [key: string]: string;
4
4
  };
5
+ serviceTag?: string;
5
6
  }
6
7
  export declare function jaypieLambdaEnv(options?: JaypieLambdaEnvOptions): {
7
8
  [key: string]: string;
@@ -0,0 +1,33 @@
1
+ import * as wafv2 from "aws-cdk-lib/aws-wafv2";
2
+ /**
3
+ * Canonical sub-rule names for each AWS managed rule group, as published in the
4
+ * AWS WAF developer guide. Used to validate `waf.allow` and
5
+ * `waf.managedRuleOverrides` rule names at synth time — AWS WAF matches
6
+ * `RuleActionOverride` on the exact rule *name* and silently ignores names that
7
+ * match no rule, so a typo or a label/name casing mismatch (e.g. the label
8
+ * `…:NoUserAgent_Header` vs the rule name `NoUserAgent_HEADER`) becomes an
9
+ * undiagnosable no-op.
10
+ *
11
+ * Groups absent from this map (custom rule groups, or AWS groups not yet
12
+ * mirrored here) are not validated.
13
+ *
14
+ * @see https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html
15
+ */
16
+ export declare const AWS_MANAGED_RULE_GROUPS: Record<string, readonly string[]>;
17
+ /** One entry in a `waf.allow` list. Mirrors JaypieWafAllowEntry structurally. */
18
+ interface WafAllowEntryLike {
19
+ path: string | string[];
20
+ [ruleGroupKey: string]: string | string[] | undefined;
21
+ }
22
+ interface AssertValidWafRuleNamesOptions {
23
+ allow?: WafAllowEntryLike | WafAllowEntryLike[];
24
+ managedRuleOverrides?: Record<string, wafv2.CfnWebACL.RuleActionOverrideProperty[]>;
25
+ }
26
+ /**
27
+ * Throw a ConfigurationError if any `waf.allow` or `waf.managedRuleOverrides`
28
+ * rule name does not exist in its AWS managed rule group. Groups not present in
29
+ * AWS_MANAGED_RULE_GROUPS (custom groups) are skipped. A name that matches no
30
+ * rule would otherwise be silently ignored by AWS WAF.
31
+ */
32
+ export declare function assertValidWafRuleNames({ allow, managedRuleOverrides, }?: AssertValidWafRuleNamesOptions): void;
33
+ export {};
package/dist/esm/index.js CHANGED
@@ -702,7 +702,7 @@ function isValidSubdomain(subdomain) {
702
702
  }
703
703
 
704
704
  function jaypieLambdaEnv(options = {}) {
705
- const { initialEnvironment = {} } = options;
705
+ const { initialEnvironment = {}, serviceTag } = options;
706
706
  // Start with empty environment - we'll only add valid values
707
707
  let environment = {};
708
708
  // First, add all valid string values from initialEnvironment
@@ -731,6 +731,11 @@ function jaypieLambdaEnv(options = {}) {
731
731
  environment[key] = defaultValue;
732
732
  }
733
733
  });
734
+ // Apply serviceTag as PROJECT_SERVICE unless explicitly overridden.
735
+ // Precedence: explicit environment > serviceTag > process.env.PROJECT_SERVICE
736
+ if (serviceTag && !environment.PROJECT_SERVICE) {
737
+ environment.PROJECT_SERVICE = serviceTag;
738
+ }
734
739
  // Default environment variables from process.env if present
735
740
  const defaultEnvVars = [
736
741
  "DATADOG_API_KEY_ARN",
@@ -1267,6 +1272,148 @@ function clearAllSecretsCaches() {
1267
1272
  // between test runs. For testing, use clearSecretsCache(scope) instead.
1268
1273
  }
1269
1274
 
1275
+ /**
1276
+ * Canonical sub-rule names for each AWS managed rule group, as published in the
1277
+ * AWS WAF developer guide. Used to validate `waf.allow` and
1278
+ * `waf.managedRuleOverrides` rule names at synth time — AWS WAF matches
1279
+ * `RuleActionOverride` on the exact rule *name* and silently ignores names that
1280
+ * match no rule, so a typo or a label/name casing mismatch (e.g. the label
1281
+ * `…:NoUserAgent_Header` vs the rule name `NoUserAgent_HEADER`) becomes an
1282
+ * undiagnosable no-op.
1283
+ *
1284
+ * Groups absent from this map (custom rule groups, or AWS groups not yet
1285
+ * mirrored here) are not validated.
1286
+ *
1287
+ * @see https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html
1288
+ */
1289
+ const AWS_MANAGED_RULE_GROUPS = {
1290
+ AWSManagedRulesAdminProtectionRuleSet: ["AdminProtection_URIPATH"],
1291
+ AWSManagedRulesAmazonIpReputationList: [
1292
+ "AWSManagedIPDDoSList",
1293
+ "AWSManagedIPReputationList",
1294
+ "AWSManagedReconnaissanceList",
1295
+ ],
1296
+ AWSManagedRulesAnonymousIpList: ["AnonymousIPList", "HostingProviderIPList"],
1297
+ AWSManagedRulesCommonRuleSet: [
1298
+ "CrossSiteScripting_BODY",
1299
+ "CrossSiteScripting_COOKIE",
1300
+ "CrossSiteScripting_QUERYARGUMENTS",
1301
+ "CrossSiteScripting_URIPATH",
1302
+ "EC2MetaDataSSRF_BODY",
1303
+ "EC2MetaDataSSRF_COOKIE",
1304
+ "EC2MetaDataSSRF_QUERYARGUMENTS",
1305
+ "EC2MetaDataSSRF_URIPATH",
1306
+ "GenericLFI_BODY",
1307
+ "GenericLFI_QUERYARGUMENTS",
1308
+ "GenericLFI_URIPATH",
1309
+ "GenericRFI_BODY",
1310
+ "GenericRFI_QUERYARGUMENTS",
1311
+ "GenericRFI_URIPATH",
1312
+ "NoUserAgent_HEADER",
1313
+ "RestrictedExtensions_QUERYARGUMENTS",
1314
+ "RestrictedExtensions_URIPATH",
1315
+ "SizeRestrictions_BODY",
1316
+ "SizeRestrictions_Cookie_HEADER",
1317
+ "SizeRestrictions_QUERYSTRING",
1318
+ "SizeRestrictions_URIPATH",
1319
+ "UserAgent_BadBots_HEADER",
1320
+ ],
1321
+ AWSManagedRulesKnownBadInputsRuleSet: [
1322
+ "ExploitablePaths_URIPATH",
1323
+ "Host_localhost_HEADER",
1324
+ "JavaDeserializationRCE_BODY",
1325
+ "JavaDeserializationRCE_HEADER",
1326
+ "JavaDeserializationRCE_QUERYSTRING",
1327
+ "JavaDeserializationRCE_URIPATH",
1328
+ "Log4JRCE_BODY",
1329
+ "Log4JRCE_HEADER",
1330
+ "Log4JRCE_QUERYSTRING",
1331
+ "Log4JRCE_URIPATH",
1332
+ "PROPFIND_METHOD",
1333
+ "ReactJSRCE_BODY",
1334
+ ],
1335
+ AWSManagedRulesLinuxRuleSet: ["LFI_HEADER", "LFI_QUERYSTRING", "LFI_URIPATH"],
1336
+ AWSManagedRulesPHPRuleSet: [
1337
+ "PHPHighRiskMethodsVariables_BODY",
1338
+ "PHPHighRiskMethodsVariables_HEADER",
1339
+ "PHPHighRiskMethodsVariables_QUERYSTRING",
1340
+ "PHPHighRiskMethodsVariables_URIPATH",
1341
+ ],
1342
+ AWSManagedRulesSQLiRuleSet: [
1343
+ "SQLiExtendedPatterns_BODY",
1344
+ "SQLiExtendedPatterns_HEADER",
1345
+ "SQLiExtendedPatterns_QUERYARGUMENTS",
1346
+ "SQLiExtendedPatterns_URIPATH",
1347
+ "SQLi_BODY",
1348
+ "SQLi_COOKIE",
1349
+ "SQLi_QUERYARGUMENTS",
1350
+ "SQLi_URIPATH",
1351
+ ],
1352
+ AWSManagedRulesUnixRuleSet: [
1353
+ "UNIXShellCommandsVariables_BODY",
1354
+ "UNIXShellCommandsVariables_HEADER",
1355
+ "UNIXShellCommandsVariables_QUERYSTRING",
1356
+ ],
1357
+ AWSManagedRulesWindowsRuleSet: [
1358
+ "PowerShellCommands_BODY",
1359
+ "PowerShellCommands_COOKIE",
1360
+ "PowerShellCommands_QUERYARGUMENTS",
1361
+ "WindowsShellCommands_BODY",
1362
+ "WindowsShellCommands_HEADER",
1363
+ "WindowsShellCommands_QUERYARGUMENTS",
1364
+ "WindowsShellCommands_QUERYSTRING",
1365
+ "WindowsShellCommands_URIPATH",
1366
+ ],
1367
+ AWSManagedRulesWordPressRuleSet: [
1368
+ "WordPressExploitableCommands_QUERYSTRING",
1369
+ "WordPressExploitablePaths_URIPATH",
1370
+ ],
1371
+ };
1372
+ /**
1373
+ * Throw a ConfigurationError if any `waf.allow` or `waf.managedRuleOverrides`
1374
+ * rule name does not exist in its AWS managed rule group. Groups not present in
1375
+ * AWS_MANAGED_RULE_GROUPS (custom groups) are skipped. A name that matches no
1376
+ * rule would otherwise be silently ignored by AWS WAF.
1377
+ */
1378
+ function assertValidWafRuleNames({ allow, managedRuleOverrides, } = {}) {
1379
+ // Collect (group → referenced rule name) pairs from both inputs.
1380
+ const references = [];
1381
+ if (managedRuleOverrides) {
1382
+ for (const [group, overrides] of Object.entries(managedRuleOverrides)) {
1383
+ for (const override of overrides ?? []) {
1384
+ if (override?.name)
1385
+ references.push({ group, ruleName: override.name });
1386
+ }
1387
+ }
1388
+ }
1389
+ const allowEntries = allow ? (Array.isArray(allow) ? allow : [allow]) : [];
1390
+ for (const entry of allowEntries) {
1391
+ for (const key of Object.keys(entry)) {
1392
+ if (key === "path")
1393
+ continue;
1394
+ const raw = entry[key];
1395
+ if (raw == null)
1396
+ continue;
1397
+ const ruleNames = Array.isArray(raw) ? raw : [raw];
1398
+ for (const ruleName of ruleNames) {
1399
+ references.push({ group: key, ruleName });
1400
+ }
1401
+ }
1402
+ }
1403
+ for (const { group, ruleName } of references) {
1404
+ const validNames = AWS_MANAGED_RULE_GROUPS[group];
1405
+ if (!validNames)
1406
+ continue; // Unknown/custom group — cannot validate
1407
+ if (!validNames.includes(ruleName)) {
1408
+ throw new ConfigurationError(`WAF rule "${ruleName}" is not a rule in ${group}. AWS WAF matches ` +
1409
+ `RuleActionOverrides on the exact rule name and silently ignores ` +
1410
+ `unmatched names (note the label/name casing trap, e.g. ` +
1411
+ `"NoUserAgent_HEADER" not "NoUserAgent_Header"). Valid rule names: ` +
1412
+ `${validNames.join(", ")}.`);
1413
+ }
1414
+ }
1415
+ }
1416
+
1270
1417
  class JaypieApiGateway extends Construct {
1271
1418
  constructor(scope, id, props) {
1272
1419
  super(scope, id);
@@ -1460,7 +1607,7 @@ class JaypieLambda extends Construct {
1460
1607
  // Resolve environment from array or object syntax
1461
1608
  const initialEnvironment = resolveEnvironment(environmentInput);
1462
1609
  // Get base environment with defaults
1463
- const environment = jaypieLambdaEnv({ initialEnvironment });
1610
+ const environment = jaypieLambdaEnv({ initialEnvironment, serviceTag });
1464
1611
  // Resolve secrets from mixed array (strings and JaypieSecret instances)
1465
1612
  // Use Stack.of(this) to ensure secrets are shared at stack level across all constructs
1466
1613
  const secrets = resolveSecrets(Stack.of(this), secretsInput);
@@ -2760,6 +2907,8 @@ class JaypieDistribution extends Construct {
2760
2907
  else {
2761
2908
  // Create new WebACL
2762
2909
  const { allow, managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES$1, rateLimitPerIp = DEFAULT_RATE_LIMIT$1, } = wafConfig;
2910
+ // Fail synth on rule names AWS WAF would silently ignore (#362)
2911
+ assertValidWafRuleNames({ allow, managedRuleOverrides });
2763
2912
  const allowEntries = allow
2764
2913
  ? Array.isArray(allow)
2765
2914
  ? allow
@@ -4746,6 +4895,8 @@ class JaypieWebDeploymentBucket extends Construct {
4746
4895
  }
4747
4896
  else {
4748
4897
  const { managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES, rateLimitPerIp = DEFAULT_RATE_LIMIT, } = wafConfig;
4898
+ // Fail synth on rule names AWS WAF would silently ignore (#362)
4899
+ assertValidWafRuleNames({ managedRuleOverrides });
4749
4900
  let priority = 0;
4750
4901
  const rules = [];
4751
4902
  for (const ruleName of managedRules) {
@@ -5419,5 +5570,5 @@ class JaypieWebSocketTable extends Construct {
5419
5570
  }
5420
5571
  }
5421
5572
 
5422
- export { CDK$2 as CDK, JaypieAccountLoggingBucket, JaypieApiGateway, JaypieAppStack, JaypieBucketQueuedLambda, JaypieCertificate, JaypieDatadogBucket, JaypieDatadogForwarder, JaypieDatadogSecret, JaypieDistribution, JaypieDnsRecord, JaypieDynamoDb, JaypieEnvSecret, JaypieEventsRule, JaypieExpressLambda, JaypieGitHubDeployRole, JaypieHostedZone, JaypieInfrastructureStack, JaypieLambda, JaypieMigration, JaypieMongoDbSecret, JaypieNextJs, JaypieOpenAiSecret, JaypieOrganizationTrail, JaypieQueuedLambda, JaypieSecret, JaypieSsoPermissions, JaypieSsoSyncApplication, JaypieStack, JaypieStaticWebBucket, JaypieTraceSigningKeySecret, JaypieWebDeploymentBucket, JaypieWebSocket, JaypieWebSocketLambda, JaypieWebSocketTable, addDatadogLayers, clearAllCertificateCaches, clearAllSecretsCaches, clearCertificateCache, clearSecretsCache, constructEnvName, constructStackName, constructTagger, constructWafLogBucketName, ensureRoute53QueryLoggingPolicy, envHostname, extendDatadogRole, isEnv, isProductionEnv, isSandboxEnv, isValidHostname$1 as isValidHostname, isValidSubdomain, jaypieLambdaEnv, mergeDomain, resolveCertificate, resolveDatadogForwarderFunction, resolveDatadogLayers, resolveDatadogLoggingDestination, resolveEnvironment, resolveHostedZone, resolveParamsAndSecrets, resolveSecrets };
5573
+ export { AWS_MANAGED_RULE_GROUPS, CDK$2 as CDK, JaypieAccountLoggingBucket, JaypieApiGateway, JaypieAppStack, JaypieBucketQueuedLambda, JaypieCertificate, JaypieDatadogBucket, JaypieDatadogForwarder, JaypieDatadogSecret, JaypieDistribution, JaypieDnsRecord, JaypieDynamoDb, JaypieEnvSecret, JaypieEventsRule, JaypieExpressLambda, JaypieGitHubDeployRole, JaypieHostedZone, JaypieInfrastructureStack, JaypieLambda, JaypieMigration, JaypieMongoDbSecret, JaypieNextJs, JaypieOpenAiSecret, JaypieOrganizationTrail, JaypieQueuedLambda, JaypieSecret, JaypieSsoPermissions, JaypieSsoSyncApplication, JaypieStack, JaypieStaticWebBucket, JaypieTraceSigningKeySecret, JaypieWebDeploymentBucket, JaypieWebSocket, JaypieWebSocketLambda, JaypieWebSocketTable, addDatadogLayers, assertValidWafRuleNames, clearAllCertificateCaches, clearAllSecretsCaches, clearCertificateCache, clearSecretsCache, constructEnvName, constructStackName, constructTagger, constructWafLogBucketName, ensureRoute53QueryLoggingPolicy, envHostname, extendDatadogRole, isEnv, isProductionEnv, isSandboxEnv, isValidHostname$1 as isValidHostname, isValidSubdomain, jaypieLambdaEnv, mergeDomain, resolveCertificate, resolveDatadogForwarderFunction, resolveDatadogLayers, resolveDatadogLoggingDestination, resolveEnvironment, resolveHostedZone, resolveParamsAndSecrets, resolveSecrets };
5423
5574
  //# sourceMappingURL=index.js.map