@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.
- package/dist/cjs/helpers/index.d.ts +1 -0
- package/dist/cjs/helpers/jaypieLambdaEnv.d.ts +1 -0
- package/dist/cjs/helpers/wafManagedRuleNames.d.ts +33 -0
- package/dist/cjs/index.cjs +155 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/helpers/index.d.ts +1 -0
- package/dist/esm/helpers/jaypieLambdaEnv.d.ts +1 -0
- package/dist/esm/helpers/wafManagedRuleNames.d.ts +33 -0
- package/dist/esm/index.js +154 -3
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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";
|
|
@@ -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/cjs/index.cjs
CHANGED
|
@@ -738,7 +738,7 @@ function isValidSubdomain(subdomain) {
|
|
|
738
738
|
}
|
|
739
739
|
|
|
740
740
|
function jaypieLambdaEnv(options = {}) {
|
|
741
|
-
const { initialEnvironment = {} } = options;
|
|
741
|
+
const { initialEnvironment = {}, serviceTag } = options;
|
|
742
742
|
// Start with empty environment - we'll only add valid values
|
|
743
743
|
let environment = {};
|
|
744
744
|
// First, add all valid string values from initialEnvironment
|
|
@@ -767,6 +767,11 @@ function jaypieLambdaEnv(options = {}) {
|
|
|
767
767
|
environment[key] = defaultValue;
|
|
768
768
|
}
|
|
769
769
|
});
|
|
770
|
+
// Apply serviceTag as PROJECT_SERVICE unless explicitly overridden.
|
|
771
|
+
// Precedence: explicit environment > serviceTag > process.env.PROJECT_SERVICE
|
|
772
|
+
if (serviceTag && !environment.PROJECT_SERVICE) {
|
|
773
|
+
environment.PROJECT_SERVICE = serviceTag;
|
|
774
|
+
}
|
|
770
775
|
// Default environment variables from process.env if present
|
|
771
776
|
const defaultEnvVars = [
|
|
772
777
|
"DATADOG_API_KEY_ARN",
|
|
@@ -1303,6 +1308,148 @@ function clearAllSecretsCaches() {
|
|
|
1303
1308
|
// between test runs. For testing, use clearSecretsCache(scope) instead.
|
|
1304
1309
|
}
|
|
1305
1310
|
|
|
1311
|
+
/**
|
|
1312
|
+
* Canonical sub-rule names for each AWS managed rule group, as published in the
|
|
1313
|
+
* AWS WAF developer guide. Used to validate `waf.allow` and
|
|
1314
|
+
* `waf.managedRuleOverrides` rule names at synth time — AWS WAF matches
|
|
1315
|
+
* `RuleActionOverride` on the exact rule *name* and silently ignores names that
|
|
1316
|
+
* match no rule, so a typo or a label/name casing mismatch (e.g. the label
|
|
1317
|
+
* `…:NoUserAgent_Header` vs the rule name `NoUserAgent_HEADER`) becomes an
|
|
1318
|
+
* undiagnosable no-op.
|
|
1319
|
+
*
|
|
1320
|
+
* Groups absent from this map (custom rule groups, or AWS groups not yet
|
|
1321
|
+
* mirrored here) are not validated.
|
|
1322
|
+
*
|
|
1323
|
+
* @see https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html
|
|
1324
|
+
*/
|
|
1325
|
+
const AWS_MANAGED_RULE_GROUPS = {
|
|
1326
|
+
AWSManagedRulesAdminProtectionRuleSet: ["AdminProtection_URIPATH"],
|
|
1327
|
+
AWSManagedRulesAmazonIpReputationList: [
|
|
1328
|
+
"AWSManagedIPDDoSList",
|
|
1329
|
+
"AWSManagedIPReputationList",
|
|
1330
|
+
"AWSManagedReconnaissanceList",
|
|
1331
|
+
],
|
|
1332
|
+
AWSManagedRulesAnonymousIpList: ["AnonymousIPList", "HostingProviderIPList"],
|
|
1333
|
+
AWSManagedRulesCommonRuleSet: [
|
|
1334
|
+
"CrossSiteScripting_BODY",
|
|
1335
|
+
"CrossSiteScripting_COOKIE",
|
|
1336
|
+
"CrossSiteScripting_QUERYARGUMENTS",
|
|
1337
|
+
"CrossSiteScripting_URIPATH",
|
|
1338
|
+
"EC2MetaDataSSRF_BODY",
|
|
1339
|
+
"EC2MetaDataSSRF_COOKIE",
|
|
1340
|
+
"EC2MetaDataSSRF_QUERYARGUMENTS",
|
|
1341
|
+
"EC2MetaDataSSRF_URIPATH",
|
|
1342
|
+
"GenericLFI_BODY",
|
|
1343
|
+
"GenericLFI_QUERYARGUMENTS",
|
|
1344
|
+
"GenericLFI_URIPATH",
|
|
1345
|
+
"GenericRFI_BODY",
|
|
1346
|
+
"GenericRFI_QUERYARGUMENTS",
|
|
1347
|
+
"GenericRFI_URIPATH",
|
|
1348
|
+
"NoUserAgent_HEADER",
|
|
1349
|
+
"RestrictedExtensions_QUERYARGUMENTS",
|
|
1350
|
+
"RestrictedExtensions_URIPATH",
|
|
1351
|
+
"SizeRestrictions_BODY",
|
|
1352
|
+
"SizeRestrictions_Cookie_HEADER",
|
|
1353
|
+
"SizeRestrictions_QUERYSTRING",
|
|
1354
|
+
"SizeRestrictions_URIPATH",
|
|
1355
|
+
"UserAgent_BadBots_HEADER",
|
|
1356
|
+
],
|
|
1357
|
+
AWSManagedRulesKnownBadInputsRuleSet: [
|
|
1358
|
+
"ExploitablePaths_URIPATH",
|
|
1359
|
+
"Host_localhost_HEADER",
|
|
1360
|
+
"JavaDeserializationRCE_BODY",
|
|
1361
|
+
"JavaDeserializationRCE_HEADER",
|
|
1362
|
+
"JavaDeserializationRCE_QUERYSTRING",
|
|
1363
|
+
"JavaDeserializationRCE_URIPATH",
|
|
1364
|
+
"Log4JRCE_BODY",
|
|
1365
|
+
"Log4JRCE_HEADER",
|
|
1366
|
+
"Log4JRCE_QUERYSTRING",
|
|
1367
|
+
"Log4JRCE_URIPATH",
|
|
1368
|
+
"PROPFIND_METHOD",
|
|
1369
|
+
"ReactJSRCE_BODY",
|
|
1370
|
+
],
|
|
1371
|
+
AWSManagedRulesLinuxRuleSet: ["LFI_HEADER", "LFI_QUERYSTRING", "LFI_URIPATH"],
|
|
1372
|
+
AWSManagedRulesPHPRuleSet: [
|
|
1373
|
+
"PHPHighRiskMethodsVariables_BODY",
|
|
1374
|
+
"PHPHighRiskMethodsVariables_HEADER",
|
|
1375
|
+
"PHPHighRiskMethodsVariables_QUERYSTRING",
|
|
1376
|
+
"PHPHighRiskMethodsVariables_URIPATH",
|
|
1377
|
+
],
|
|
1378
|
+
AWSManagedRulesSQLiRuleSet: [
|
|
1379
|
+
"SQLiExtendedPatterns_BODY",
|
|
1380
|
+
"SQLiExtendedPatterns_HEADER",
|
|
1381
|
+
"SQLiExtendedPatterns_QUERYARGUMENTS",
|
|
1382
|
+
"SQLiExtendedPatterns_URIPATH",
|
|
1383
|
+
"SQLi_BODY",
|
|
1384
|
+
"SQLi_COOKIE",
|
|
1385
|
+
"SQLi_QUERYARGUMENTS",
|
|
1386
|
+
"SQLi_URIPATH",
|
|
1387
|
+
],
|
|
1388
|
+
AWSManagedRulesUnixRuleSet: [
|
|
1389
|
+
"UNIXShellCommandsVariables_BODY",
|
|
1390
|
+
"UNIXShellCommandsVariables_HEADER",
|
|
1391
|
+
"UNIXShellCommandsVariables_QUERYSTRING",
|
|
1392
|
+
],
|
|
1393
|
+
AWSManagedRulesWindowsRuleSet: [
|
|
1394
|
+
"PowerShellCommands_BODY",
|
|
1395
|
+
"PowerShellCommands_COOKIE",
|
|
1396
|
+
"PowerShellCommands_QUERYARGUMENTS",
|
|
1397
|
+
"WindowsShellCommands_BODY",
|
|
1398
|
+
"WindowsShellCommands_HEADER",
|
|
1399
|
+
"WindowsShellCommands_QUERYARGUMENTS",
|
|
1400
|
+
"WindowsShellCommands_QUERYSTRING",
|
|
1401
|
+
"WindowsShellCommands_URIPATH",
|
|
1402
|
+
],
|
|
1403
|
+
AWSManagedRulesWordPressRuleSet: [
|
|
1404
|
+
"WordPressExploitableCommands_QUERYSTRING",
|
|
1405
|
+
"WordPressExploitablePaths_URIPATH",
|
|
1406
|
+
],
|
|
1407
|
+
};
|
|
1408
|
+
/**
|
|
1409
|
+
* Throw a ConfigurationError if any `waf.allow` or `waf.managedRuleOverrides`
|
|
1410
|
+
* rule name does not exist in its AWS managed rule group. Groups not present in
|
|
1411
|
+
* AWS_MANAGED_RULE_GROUPS (custom groups) are skipped. A name that matches no
|
|
1412
|
+
* rule would otherwise be silently ignored by AWS WAF.
|
|
1413
|
+
*/
|
|
1414
|
+
function assertValidWafRuleNames({ allow, managedRuleOverrides, } = {}) {
|
|
1415
|
+
// Collect (group → referenced rule name) pairs from both inputs.
|
|
1416
|
+
const references = [];
|
|
1417
|
+
if (managedRuleOverrides) {
|
|
1418
|
+
for (const [group, overrides] of Object.entries(managedRuleOverrides)) {
|
|
1419
|
+
for (const override of overrides ?? []) {
|
|
1420
|
+
if (override?.name)
|
|
1421
|
+
references.push({ group, ruleName: override.name });
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
const allowEntries = allow ? (Array.isArray(allow) ? allow : [allow]) : [];
|
|
1426
|
+
for (const entry of allowEntries) {
|
|
1427
|
+
for (const key of Object.keys(entry)) {
|
|
1428
|
+
if (key === "path")
|
|
1429
|
+
continue;
|
|
1430
|
+
const raw = entry[key];
|
|
1431
|
+
if (raw == null)
|
|
1432
|
+
continue;
|
|
1433
|
+
const ruleNames = Array.isArray(raw) ? raw : [raw];
|
|
1434
|
+
for (const ruleName of ruleNames) {
|
|
1435
|
+
references.push({ group: key, ruleName });
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
for (const { group, ruleName } of references) {
|
|
1440
|
+
const validNames = AWS_MANAGED_RULE_GROUPS[group];
|
|
1441
|
+
if (!validNames)
|
|
1442
|
+
continue; // Unknown/custom group — cannot validate
|
|
1443
|
+
if (!validNames.includes(ruleName)) {
|
|
1444
|
+
throw new errors.ConfigurationError(`WAF rule "${ruleName}" is not a rule in ${group}. AWS WAF matches ` +
|
|
1445
|
+
`RuleActionOverrides on the exact rule name and silently ignores ` +
|
|
1446
|
+
`unmatched names (note the label/name casing trap, e.g. ` +
|
|
1447
|
+
`"NoUserAgent_HEADER" not "NoUserAgent_Header"). Valid rule names: ` +
|
|
1448
|
+
`${validNames.join(", ")}.`);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1306
1453
|
class JaypieApiGateway extends constructs.Construct {
|
|
1307
1454
|
constructor(scope, id, props) {
|
|
1308
1455
|
super(scope, id);
|
|
@@ -1496,7 +1643,7 @@ class JaypieLambda extends constructs.Construct {
|
|
|
1496
1643
|
// Resolve environment from array or object syntax
|
|
1497
1644
|
const initialEnvironment = resolveEnvironment(environmentInput);
|
|
1498
1645
|
// Get base environment with defaults
|
|
1499
|
-
const environment = jaypieLambdaEnv({ initialEnvironment });
|
|
1646
|
+
const environment = jaypieLambdaEnv({ initialEnvironment, serviceTag });
|
|
1500
1647
|
// Resolve secrets from mixed array (strings and JaypieSecret instances)
|
|
1501
1648
|
// Use Stack.of(this) to ensure secrets are shared at stack level across all constructs
|
|
1502
1649
|
const secrets = resolveSecrets(cdk.Stack.of(this), secretsInput);
|
|
@@ -2796,6 +2943,8 @@ class JaypieDistribution extends constructs.Construct {
|
|
|
2796
2943
|
else {
|
|
2797
2944
|
// Create new WebACL
|
|
2798
2945
|
const { allow, managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES$1, rateLimitPerIp = DEFAULT_RATE_LIMIT$1, } = wafConfig;
|
|
2946
|
+
// Fail synth on rule names AWS WAF would silently ignore (#362)
|
|
2947
|
+
assertValidWafRuleNames({ allow, managedRuleOverrides });
|
|
2799
2948
|
const allowEntries = allow
|
|
2800
2949
|
? Array.isArray(allow)
|
|
2801
2950
|
? allow
|
|
@@ -4782,6 +4931,8 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
4782
4931
|
}
|
|
4783
4932
|
else {
|
|
4784
4933
|
const { managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES, rateLimitPerIp = DEFAULT_RATE_LIMIT, } = wafConfig;
|
|
4934
|
+
// Fail synth on rule names AWS WAF would silently ignore (#362)
|
|
4935
|
+
assertValidWafRuleNames({ managedRuleOverrides });
|
|
4785
4936
|
let priority = 0;
|
|
4786
4937
|
const rules = [];
|
|
4787
4938
|
for (const ruleName of managedRules) {
|
|
@@ -5455,6 +5606,7 @@ class JaypieWebSocketTable extends constructs.Construct {
|
|
|
5455
5606
|
}
|
|
5456
5607
|
}
|
|
5457
5608
|
|
|
5609
|
+
exports.AWS_MANAGED_RULE_GROUPS = AWS_MANAGED_RULE_GROUPS;
|
|
5458
5610
|
exports.CDK = CDK$2;
|
|
5459
5611
|
exports.JaypieAccountLoggingBucket = JaypieAccountLoggingBucket;
|
|
5460
5612
|
exports.JaypieApiGateway = JaypieApiGateway;
|
|
@@ -5491,6 +5643,7 @@ exports.JaypieWebSocket = JaypieWebSocket;
|
|
|
5491
5643
|
exports.JaypieWebSocketLambda = JaypieWebSocketLambda;
|
|
5492
5644
|
exports.JaypieWebSocketTable = JaypieWebSocketTable;
|
|
5493
5645
|
exports.addDatadogLayers = addDatadogLayers;
|
|
5646
|
+
exports.assertValidWafRuleNames = assertValidWafRuleNames;
|
|
5494
5647
|
exports.clearAllCertificateCaches = clearAllCertificateCaches;
|
|
5495
5648
|
exports.clearAllSecretsCaches = clearAllSecretsCaches;
|
|
5496
5649
|
exports.clearCertificateCache = clearCertificateCache;
|