@jaypie/constructs 1.2.58 → 1.2.60

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.
@@ -973,115 +973,67 @@ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, } = {}) => {
973
973
  return resolvedParamsAndSecrets;
974
974
  };
975
975
 
976
- // It is a consumer if the environment is ephemeral
977
- function checkEnvIsConsumer$1(env = process.env) {
978
- return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
979
- !!env.CDK_ENV_PERSONAL ||
980
- /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
981
- /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
982
- }
983
- function checkEnvIsProvider$1(env = process.env) {
984
- return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
985
- }
986
- function cleanName$1(name) {
987
- return name.replace(/[^a-zA-Z0-9:-]/g, "");
988
- }
989
- function exportEnvName$1(name, env = process.env, consumer = false) {
990
- let rawName;
991
- if (checkEnvIsProvider$1(env)) {
992
- rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
993
- // Clean the entire name to only allow alphanumeric, colons, and hyphens
994
- return cleanName$1(rawName);
995
- }
996
- else {
997
- if (consumer || checkEnvIsConsumer$1(env)) {
998
- rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
999
- }
1000
- else {
1001
- rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
1002
- }
1003
- }
1004
- return cleanName$1(rawName);
1005
- }
1006
- class JaypieEnvSecret extends constructs.Construct {
976
+ class JaypieSecret extends constructs.Construct {
1007
977
  constructor(scope, idOrEnvKey, props) {
1008
978
  // Shorthand detection: treat idOrEnvKey as envKey when envKey prop is
1009
979
  // not set and idOrEnvKey either looks like a SCREAMING_SNAKE_CASE env
1010
980
  // var name or is already present in process.env. Convention-based
1011
981
  // detection ensures missing env vars still go through envKey validation
1012
982
  // instead of silently creating an empty secret.
983
+ const prefix = new.target.shorthandPrefix;
1013
984
  const looksLikeEnvKey = /^[A-Z][A-Z0-9_]*$/.test(idOrEnvKey);
1014
985
  const treatAsEnvKey = (!props || props.envKey === undefined) &&
1015
986
  (looksLikeEnvKey ||
1016
987
  (typeof process.env[idOrEnvKey] === "string" &&
1017
988
  process.env[idOrEnvKey] !== ""));
1018
- const id = treatAsEnvKey ? `EnvSecret_${idOrEnvKey}` : idOrEnvKey;
989
+ const id = treatAsEnvKey ? `${prefix}${idOrEnvKey}` : idOrEnvKey;
1019
990
  super(scope, id);
1020
- const { consumer = checkEnvIsConsumer$1(), envKey: envKeyProp, export: exportParam, generateSecretString, provider = checkEnvIsProvider$1(), removalPolicy, roleTag, vendorTag, value, } = props || {};
1021
- const envKey = treatAsEnvKey ? idOrEnvKey : envKeyProp;
991
+ const envKey = treatAsEnvKey ? idOrEnvKey : props?.envKey;
1022
992
  this._envKey = envKey;
1023
- let exportName;
1024
- if (!exportParam) {
1025
- // When shorthand detection is active, use the full construct id (which
1026
- // includes the "EnvSecret_" prefix) so the export name matches what was
1027
- // produced by earlier versions of this construct. Using the raw envKey
1028
- // here produces a shorter name that breaks existing cross-stack imports.
1029
- const exportSource = treatAsEnvKey ? id : envKey || id;
1030
- exportName = exportEnvName$1(exportSource, process.env, consumer);
1031
- }
1032
- else {
1033
- exportName = cleanName$1(exportParam);
1034
- }
1035
- if (!consumer &&
1036
- envKey &&
993
+ this._secret = this.buildSecret({
994
+ envKey,
995
+ id,
996
+ props: props || {},
997
+ treatAsEnvKey,
998
+ });
999
+ }
1000
+ /**
1001
+ * Builds the underlying secret. The base implementation always creates a new
1002
+ * Secrets Manager secret from an envKey value, an explicit value, or a
1003
+ * generated string. Subclasses may override to import an existing secret or
1004
+ * emit cross-stack outputs.
1005
+ */
1006
+ buildSecret(context) {
1007
+ const { envKey, id, props } = context;
1008
+ const { generateSecretString, removalPolicy, roleTag, vendorTag, value } = props;
1009
+ if (envKey &&
1037
1010
  !process.env[envKey] &&
1038
1011
  value === undefined &&
1039
1012
  !generateSecretString) {
1040
- throw new errors.ConfigurationError(`JaypieEnvSecret(${id}): envKey "${envKey}" is empty in process.env and no value or generateSecretString was provided`);
1013
+ throw new errors.ConfigurationError(`JaypieSecret(${id}): envKey "${envKey}" is empty in process.env and no value or generateSecretString was provided`);
1041
1014
  }
1042
- if (consumer) {
1043
- const secretName = cdk.Fn.importValue(exportName);
1044
- this._secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
1045
- // Add CfnOutput for consumer secrets
1046
- new cdk.CfnOutput(this, `ConsumedName`, {
1047
- value: this._secret.secretName,
1048
- });
1015
+ const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
1016
+ const secret = new secretsmanager__namespace.Secret(this, id, {
1017
+ generateSecretString,
1018
+ secretStringValue: !generateSecretString && secretValue
1019
+ ? cdk.SecretValue.unsafePlainText(secretValue)
1020
+ : undefined,
1021
+ });
1022
+ if (removalPolicy !== undefined) {
1023
+ const policy = typeof removalPolicy === "boolean"
1024
+ ? removalPolicy
1025
+ ? cdk.RemovalPolicy.RETAIN
1026
+ : cdk.RemovalPolicy.DESTROY
1027
+ : removalPolicy;
1028
+ secret.applyRemovalPolicy(policy);
1049
1029
  }
1050
- else {
1051
- const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
1052
- const secretProps = {
1053
- generateSecretString,
1054
- secretStringValue: !generateSecretString && secretValue
1055
- ? cdk.SecretValue.unsafePlainText(secretValue)
1056
- : undefined,
1057
- };
1058
- this._secret = new secretsmanager__namespace.Secret(this, id, secretProps);
1059
- if (removalPolicy !== undefined) {
1060
- const policy = typeof removalPolicy === "boolean"
1061
- ? removalPolicy
1062
- ? cdk.RemovalPolicy.RETAIN
1063
- : cdk.RemovalPolicy.DESTROY
1064
- : removalPolicy;
1065
- this._secret.applyRemovalPolicy(policy);
1066
- }
1067
- if (roleTag) {
1068
- cdk.Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
1069
- }
1070
- if (vendorTag) {
1071
- cdk.Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
1072
- }
1073
- if (provider) {
1074
- new cdk.CfnOutput(this, `ProvidedName`, {
1075
- value: this._secret.secretName,
1076
- exportName,
1077
- });
1078
- }
1079
- else {
1080
- new cdk.CfnOutput(this, `CreatedName`, {
1081
- value: this._secret.secretName,
1082
- });
1083
- }
1030
+ if (roleTag) {
1031
+ cdk.Tags.of(secret).add(CDK$2.TAG.ROLE, roleTag);
1032
+ }
1033
+ if (vendorTag) {
1034
+ cdk.Tags.of(secret).add(CDK$2.TAG.VENDOR, vendorTag);
1084
1035
  }
1036
+ return secret;
1085
1037
  }
1086
1038
  // IResource implementation
1087
1039
  get stack() {
@@ -1143,6 +1095,117 @@ class JaypieEnvSecret extends constructs.Construct {
1143
1095
  return this._envKey;
1144
1096
  }
1145
1097
  }
1098
+ // Construct id prefix used when an envKey is detected via shorthand.
1099
+ // Subclasses override this to preserve their own naming conventions.
1100
+ JaypieSecret.shorthandPrefix = "Secret_";
1101
+
1102
+ // It is a consumer if the environment is ephemeral
1103
+ function checkEnvIsConsumer$1(env = process.env) {
1104
+ return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
1105
+ !!env.CDK_ENV_PERSONAL ||
1106
+ /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
1107
+ /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
1108
+ }
1109
+ function checkEnvIsProvider$1(env = process.env) {
1110
+ return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
1111
+ }
1112
+ function cleanName$1(name) {
1113
+ return name.replace(/[^a-zA-Z0-9:-]/g, "");
1114
+ }
1115
+ function exportEnvName$1(name, env = process.env, consumer = false) {
1116
+ let rawName;
1117
+ if (checkEnvIsProvider$1(env)) {
1118
+ rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
1119
+ // Clean the entire name to only allow alphanumeric, colons, and hyphens
1120
+ return cleanName$1(rawName);
1121
+ }
1122
+ else {
1123
+ if (consumer || checkEnvIsConsumer$1(env)) {
1124
+ rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
1125
+ }
1126
+ else {
1127
+ rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
1128
+ }
1129
+ }
1130
+ return cleanName$1(rawName);
1131
+ }
1132
+ /**
1133
+ * @deprecated Use {@link JaypieSecret}. JaypieEnvSecret layers an
1134
+ * environment-driven provider/consumer cross-stack pattern on top of
1135
+ * JaypieSecret and will be removed in 2.0.
1136
+ */
1137
+ class JaypieEnvSecret extends JaypieSecret {
1138
+ constructor(scope, idOrEnvKey, props) {
1139
+ super(scope, idOrEnvKey, props);
1140
+ }
1141
+ buildSecret(context) {
1142
+ const { envKey, id, treatAsEnvKey } = context;
1143
+ const props = context.props;
1144
+ const { consumer = checkEnvIsConsumer$1(), export: exportParam, generateSecretString, provider = checkEnvIsProvider$1(), removalPolicy, roleTag, vendorTag, value, } = props;
1145
+ let exportName;
1146
+ if (!exportParam) {
1147
+ // When shorthand detection is active, use the full construct id (which
1148
+ // includes the "EnvSecret_" prefix) so the export name matches what was
1149
+ // produced by earlier versions of this construct. Using the raw envKey
1150
+ // here produces a shorter name that breaks existing cross-stack imports.
1151
+ const exportSource = treatAsEnvKey ? id : envKey || id;
1152
+ exportName = exportEnvName$1(exportSource, process.env, consumer);
1153
+ }
1154
+ else {
1155
+ exportName = cleanName$1(exportParam);
1156
+ }
1157
+ if (!consumer &&
1158
+ envKey &&
1159
+ !process.env[envKey] &&
1160
+ value === undefined &&
1161
+ !generateSecretString) {
1162
+ throw new errors.ConfigurationError(`JaypieEnvSecret(${id}): envKey "${envKey}" is empty in process.env and no value or generateSecretString was provided`);
1163
+ }
1164
+ if (consumer) {
1165
+ const secretName = cdk.Fn.importValue(exportName);
1166
+ const secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
1167
+ // Add CfnOutput for consumer secrets
1168
+ new cdk.CfnOutput(this, `ConsumedName`, {
1169
+ value: secret.secretName,
1170
+ });
1171
+ return secret;
1172
+ }
1173
+ const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
1174
+ const secret = new secretsmanager__namespace.Secret(this, id, {
1175
+ generateSecretString,
1176
+ secretStringValue: !generateSecretString && secretValue
1177
+ ? cdk.SecretValue.unsafePlainText(secretValue)
1178
+ : undefined,
1179
+ });
1180
+ if (removalPolicy !== undefined) {
1181
+ const policy = typeof removalPolicy === "boolean"
1182
+ ? removalPolicy
1183
+ ? cdk.RemovalPolicy.RETAIN
1184
+ : cdk.RemovalPolicy.DESTROY
1185
+ : removalPolicy;
1186
+ secret.applyRemovalPolicy(policy);
1187
+ }
1188
+ if (roleTag) {
1189
+ cdk.Tags.of(secret).add(CDK$2.TAG.ROLE, roleTag);
1190
+ }
1191
+ if (vendorTag) {
1192
+ cdk.Tags.of(secret).add(CDK$2.TAG.VENDOR, vendorTag);
1193
+ }
1194
+ if (provider) {
1195
+ new cdk.CfnOutput(this, `ProvidedName`, {
1196
+ value: secret.secretName,
1197
+ exportName,
1198
+ });
1199
+ }
1200
+ else {
1201
+ new cdk.CfnOutput(this, `CreatedName`, {
1202
+ value: secret.secretName,
1203
+ });
1204
+ }
1205
+ return secret;
1206
+ }
1207
+ }
1208
+ JaypieEnvSecret.shorthandPrefix = "EnvSecret_";
1146
1209
 
1147
1210
  /**
1148
1211
  * Cache for secrets by scope to avoid creating duplicates.
@@ -1179,11 +1242,11 @@ function getOrCreateSecret(scope, envKey, props) {
1179
1242
  return secret;
1180
1243
  }
1181
1244
  /**
1182
- * Resolves secrets input to an array of JaypieEnvSecret instances.
1245
+ * Resolves secrets input to an array of JaypieSecret instances.
1183
1246
  *
1184
- * When an item is already a JaypieEnvSecret, it's passed through as-is.
1185
- * When an item is a string, a JaypieEnvSecret is created (or reused from cache)
1186
- * with the string as the envKey.
1247
+ * When an item is already a JaypieSecret (including a JaypieEnvSecret), it's
1248
+ * passed through as-is. When an item is a string, a JaypieEnvSecret is created
1249
+ * (or reused from cache) with the string as the envKey.
1187
1250
  *
1188
1251
  * Secrets are cached per scope to avoid creating duplicate secrets when
1189
1252
  * multiple constructs in the same scope reference the same secret.
@@ -1219,7 +1282,7 @@ function resolveSecrets(scope, secrets) {
1219
1282
  if (typeof item === "string") {
1220
1283
  return getOrCreateSecret(scope, item);
1221
1284
  }
1222
- // Already a JaypieEnvSecret instance
1285
+ // Already a JaypieSecret (or JaypieEnvSecret) instance
1223
1286
  return item;
1224
1287
  });
1225
1288
  }
@@ -1240,6 +1303,148 @@ function clearAllSecretsCaches() {
1240
1303
  // between test runs. For testing, use clearSecretsCache(scope) instead.
1241
1304
  }
1242
1305
 
1306
+ /**
1307
+ * Canonical sub-rule names for each AWS managed rule group, as published in the
1308
+ * AWS WAF developer guide. Used to validate `waf.allow` and
1309
+ * `waf.managedRuleOverrides` rule names at synth time — AWS WAF matches
1310
+ * `RuleActionOverride` on the exact rule *name* and silently ignores names that
1311
+ * match no rule, so a typo or a label/name casing mismatch (e.g. the label
1312
+ * `…:NoUserAgent_Header` vs the rule name `NoUserAgent_HEADER`) becomes an
1313
+ * undiagnosable no-op.
1314
+ *
1315
+ * Groups absent from this map (custom rule groups, or AWS groups not yet
1316
+ * mirrored here) are not validated.
1317
+ *
1318
+ * @see https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html
1319
+ */
1320
+ const AWS_MANAGED_RULE_GROUPS = {
1321
+ AWSManagedRulesAdminProtectionRuleSet: ["AdminProtection_URIPATH"],
1322
+ AWSManagedRulesAmazonIpReputationList: [
1323
+ "AWSManagedIPDDoSList",
1324
+ "AWSManagedIPReputationList",
1325
+ "AWSManagedReconnaissanceList",
1326
+ ],
1327
+ AWSManagedRulesAnonymousIpList: ["AnonymousIPList", "HostingProviderIPList"],
1328
+ AWSManagedRulesCommonRuleSet: [
1329
+ "CrossSiteScripting_BODY",
1330
+ "CrossSiteScripting_COOKIE",
1331
+ "CrossSiteScripting_QUERYARGUMENTS",
1332
+ "CrossSiteScripting_URIPATH",
1333
+ "EC2MetaDataSSRF_BODY",
1334
+ "EC2MetaDataSSRF_COOKIE",
1335
+ "EC2MetaDataSSRF_QUERYARGUMENTS",
1336
+ "EC2MetaDataSSRF_URIPATH",
1337
+ "GenericLFI_BODY",
1338
+ "GenericLFI_QUERYARGUMENTS",
1339
+ "GenericLFI_URIPATH",
1340
+ "GenericRFI_BODY",
1341
+ "GenericRFI_QUERYARGUMENTS",
1342
+ "GenericRFI_URIPATH",
1343
+ "NoUserAgent_HEADER",
1344
+ "RestrictedExtensions_QUERYARGUMENTS",
1345
+ "RestrictedExtensions_URIPATH",
1346
+ "SizeRestrictions_BODY",
1347
+ "SizeRestrictions_Cookie_HEADER",
1348
+ "SizeRestrictions_QUERYSTRING",
1349
+ "SizeRestrictions_URIPATH",
1350
+ "UserAgent_BadBots_HEADER",
1351
+ ],
1352
+ AWSManagedRulesKnownBadInputsRuleSet: [
1353
+ "ExploitablePaths_URIPATH",
1354
+ "Host_localhost_HEADER",
1355
+ "JavaDeserializationRCE_BODY",
1356
+ "JavaDeserializationRCE_HEADER",
1357
+ "JavaDeserializationRCE_QUERYSTRING",
1358
+ "JavaDeserializationRCE_URIPATH",
1359
+ "Log4JRCE_BODY",
1360
+ "Log4JRCE_HEADER",
1361
+ "Log4JRCE_QUERYSTRING",
1362
+ "Log4JRCE_URIPATH",
1363
+ "PROPFIND_METHOD",
1364
+ "ReactJSRCE_BODY",
1365
+ ],
1366
+ AWSManagedRulesLinuxRuleSet: ["LFI_HEADER", "LFI_QUERYSTRING", "LFI_URIPATH"],
1367
+ AWSManagedRulesPHPRuleSet: [
1368
+ "PHPHighRiskMethodsVariables_BODY",
1369
+ "PHPHighRiskMethodsVariables_HEADER",
1370
+ "PHPHighRiskMethodsVariables_QUERYSTRING",
1371
+ "PHPHighRiskMethodsVariables_URIPATH",
1372
+ ],
1373
+ AWSManagedRulesSQLiRuleSet: [
1374
+ "SQLiExtendedPatterns_BODY",
1375
+ "SQLiExtendedPatterns_HEADER",
1376
+ "SQLiExtendedPatterns_QUERYARGUMENTS",
1377
+ "SQLiExtendedPatterns_URIPATH",
1378
+ "SQLi_BODY",
1379
+ "SQLi_COOKIE",
1380
+ "SQLi_QUERYARGUMENTS",
1381
+ "SQLi_URIPATH",
1382
+ ],
1383
+ AWSManagedRulesUnixRuleSet: [
1384
+ "UNIXShellCommandsVariables_BODY",
1385
+ "UNIXShellCommandsVariables_HEADER",
1386
+ "UNIXShellCommandsVariables_QUERYSTRING",
1387
+ ],
1388
+ AWSManagedRulesWindowsRuleSet: [
1389
+ "PowerShellCommands_BODY",
1390
+ "PowerShellCommands_COOKIE",
1391
+ "PowerShellCommands_QUERYARGUMENTS",
1392
+ "WindowsShellCommands_BODY",
1393
+ "WindowsShellCommands_HEADER",
1394
+ "WindowsShellCommands_QUERYARGUMENTS",
1395
+ "WindowsShellCommands_QUERYSTRING",
1396
+ "WindowsShellCommands_URIPATH",
1397
+ ],
1398
+ AWSManagedRulesWordPressRuleSet: [
1399
+ "WordPressExploitableCommands_QUERYSTRING",
1400
+ "WordPressExploitablePaths_URIPATH",
1401
+ ],
1402
+ };
1403
+ /**
1404
+ * Throw a ConfigurationError if any `waf.allow` or `waf.managedRuleOverrides`
1405
+ * rule name does not exist in its AWS managed rule group. Groups not present in
1406
+ * AWS_MANAGED_RULE_GROUPS (custom groups) are skipped. A name that matches no
1407
+ * rule would otherwise be silently ignored by AWS WAF.
1408
+ */
1409
+ function assertValidWafRuleNames({ allow, managedRuleOverrides, } = {}) {
1410
+ // Collect (group → referenced rule name) pairs from both inputs.
1411
+ const references = [];
1412
+ if (managedRuleOverrides) {
1413
+ for (const [group, overrides] of Object.entries(managedRuleOverrides)) {
1414
+ for (const override of overrides ?? []) {
1415
+ if (override?.name)
1416
+ references.push({ group, ruleName: override.name });
1417
+ }
1418
+ }
1419
+ }
1420
+ const allowEntries = allow ? (Array.isArray(allow) ? allow : [allow]) : [];
1421
+ for (const entry of allowEntries) {
1422
+ for (const key of Object.keys(entry)) {
1423
+ if (key === "path")
1424
+ continue;
1425
+ const raw = entry[key];
1426
+ if (raw == null)
1427
+ continue;
1428
+ const ruleNames = Array.isArray(raw) ? raw : [raw];
1429
+ for (const ruleName of ruleNames) {
1430
+ references.push({ group: key, ruleName });
1431
+ }
1432
+ }
1433
+ }
1434
+ for (const { group, ruleName } of references) {
1435
+ const validNames = AWS_MANAGED_RULE_GROUPS[group];
1436
+ if (!validNames)
1437
+ continue; // Unknown/custom group — cannot validate
1438
+ if (!validNames.includes(ruleName)) {
1439
+ throw new errors.ConfigurationError(`WAF rule "${ruleName}" is not a rule in ${group}. AWS WAF matches ` +
1440
+ `RuleActionOverrides on the exact rule name and silently ignores ` +
1441
+ `unmatched names (note the label/name casing trap, e.g. ` +
1442
+ `"NoUserAgent_HEADER" not "NoUserAgent_Header"). Valid rule names: ` +
1443
+ `${validNames.join(", ")}.`);
1444
+ }
1445
+ }
1446
+ }
1447
+
1243
1448
  class JaypieApiGateway extends constructs.Construct {
1244
1449
  constructor(scope, id, props) {
1245
1450
  super(scope, id);
@@ -1429,12 +1634,12 @@ class JaypieLambda extends constructs.Construct {
1429
1634
  super(scope, id);
1430
1635
  const { allowAllOutbound, allowPublicSubnet, architecture = lambda__namespace.Architecture.X86_64, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, tables = [], environment: environmentInput, envSecrets = {}, ephemeralStorageSize, filesystem, handler = "index.handler", initialPolicy, layers = [], logGroup, logRetention = CDK$2.LAMBDA.LOG_RETENTION, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag = CDK$2.ROLE.PROCESSING, runtime = new lambda__namespace.Runtime("nodejs24.x", lambda__namespace.RuntimeFamily.NODEJS, {
1431
1636
  supportsInlineCode: true,
1432
- }), runtimeManagementMode, secrets: secretsInput = [], securityGroups, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
1637
+ }), runtimeManagementMode, secrets: secretsInput = [], securityGroups, serviceTag, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
1433
1638
  // Resolve environment from array or object syntax
1434
1639
  const initialEnvironment = resolveEnvironment(environmentInput);
1435
1640
  // Get base environment with defaults
1436
1641
  const environment = jaypieLambdaEnv({ initialEnvironment });
1437
- // Resolve secrets from mixed array (strings and JaypieEnvSecret instances)
1642
+ // Resolve secrets from mixed array (strings and JaypieSecret instances)
1438
1643
  // Use Stack.of(this) to ensure secrets are shared at stack level across all constructs
1439
1644
  const secrets = resolveSecrets(cdk.Stack.of(this), secretsInput);
1440
1645
  const codeAsset = typeof code === "string" ? lambda__namespace.Code.fromAsset(code) : code;
@@ -1445,7 +1650,7 @@ class JaypieLambda extends constructs.Construct {
1445
1650
  ...acc,
1446
1651
  [`SECRET_${key}`]: secret.secretName,
1447
1652
  }), {});
1448
- // Process JaypieEnvSecret array
1653
+ // Process JaypieSecret array
1449
1654
  const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
1450
1655
  if (secret.envKey) {
1451
1656
  return {
@@ -1515,7 +1720,7 @@ class JaypieLambda extends constructs.Construct {
1515
1720
  Object.values(envSecrets).forEach((secret) => {
1516
1721
  secret.grantRead(this._lambda);
1517
1722
  });
1518
- // Grant read permissions for JaypieEnvSecrets
1723
+ // Grant read permissions for JaypieSecrets
1519
1724
  secrets.forEach((secret) => {
1520
1725
  secret.grantRead(this._lambda);
1521
1726
  });
@@ -1543,6 +1748,9 @@ class JaypieLambda extends constructs.Construct {
1543
1748
  if (roleTag) {
1544
1749
  cdk.Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
1545
1750
  }
1751
+ if (serviceTag) {
1752
+ cdk.Tags.of(this._lambda).add(CDK$2.TAG.SERVICE, serviceTag);
1753
+ }
1546
1754
  if (vendorTag) {
1547
1755
  cdk.Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
1548
1756
  }
@@ -1667,7 +1875,7 @@ class JaypieQueuedLambda extends constructs.Construct {
1667
1875
  super(scope, id);
1668
1876
  const { allowAllOutbound, allowPublicSubnet, architecture, batchSize = 1, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment = {}, envSecrets = {}, ephemeralStorageSize, fifo = true, filesystem, handler = "index.handler", initialPolicy, layers = [], logGroup, logRetention = CDK$2.LAMBDA.LOG_RETENTION, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag, runtime = new lambda__namespace.Runtime("nodejs24.x", lambda__namespace.RuntimeFamily.NODEJS, {
1669
1877
  supportsInlineCode: true,
1670
- }), runtimeManagementMode, secrets = [], securityGroups, tables = [], timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, visibilityTimeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vpc, vpcSubnets, } = props;
1878
+ }), runtimeManagementMode, secrets = [], securityGroups, serviceTag, tables = [], timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, visibilityTimeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vpc, vpcSubnets, } = props;
1671
1879
  // Create SQS Queue
1672
1880
  this._queue = new sqs__namespace.Queue(this, "Queue", {
1673
1881
  fifo,
@@ -1678,6 +1886,9 @@ class JaypieQueuedLambda extends constructs.Construct {
1678
1886
  if (roleTag) {
1679
1887
  cdk.Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
1680
1888
  }
1889
+ if (serviceTag) {
1890
+ cdk.Tags.of(this._queue).add(CDK$2.TAG.SERVICE, serviceTag);
1891
+ }
1681
1892
  if (vendorTag) {
1682
1893
  cdk.Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
1683
1894
  }
@@ -1715,6 +1926,7 @@ class JaypieQueuedLambda extends constructs.Construct {
1715
1926
  runtimeManagementMode,
1716
1927
  secrets,
1717
1928
  securityGroups,
1929
+ serviceTag,
1718
1930
  tables,
1719
1931
  timeout,
1720
1932
  tracing,
@@ -1909,7 +2121,7 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
1909
2121
  constructor(scope, id, props) {
1910
2122
  props.fifo = false; // S3 event notifications are not supported for FIFO queues
1911
2123
  super(scope, id, props);
1912
- const { bucketName, roleTag, vendorTag, bucketOptions = {} } = props;
2124
+ const { bucketName, roleTag, serviceTag, vendorTag, bucketOptions = {}, } = props;
1913
2125
  // Create S3 Bucket
1914
2126
  this._bucket = new s3__namespace.Bucket(this, "Bucket", {
1915
2127
  bucketName: bucketOptions.bucketName || bucketName,
@@ -1920,6 +2132,9 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
1920
2132
  if (roleTag) {
1921
2133
  cdk.Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
1922
2134
  }
2135
+ if (serviceTag) {
2136
+ cdk.Tags.of(this._bucket).add(CDK$2.TAG.SERVICE, serviceTag);
2137
+ }
1923
2138
  if (vendorTag) {
1924
2139
  cdk.Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
1925
2140
  }
@@ -2723,6 +2938,8 @@ class JaypieDistribution extends constructs.Construct {
2723
2938
  else {
2724
2939
  // Create new WebACL
2725
2940
  const { allow, managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES$1, rateLimitPerIp = DEFAULT_RATE_LIMIT$1, } = wafConfig;
2941
+ // Fail synth on rule names AWS WAF would silently ignore (#362)
2942
+ assertValidWafRuleNames({ allow, managedRuleOverrides });
2726
2943
  const allowEntries = allow
2727
2944
  ? Array.isArray(allow)
2728
2945
  ? allow
@@ -3757,7 +3974,7 @@ class JaypieNextJs extends constructs.Construct {
3757
3974
  ? path__namespace.join(process.cwd(), props.nextjsPath)
3758
3975
  : props?.nextjsPath || path__namespace.join(process.cwd(), "..", "nextjs");
3759
3976
  const paramsAndSecrets = resolveParamsAndSecrets();
3760
- // Resolve secrets from mixed array (strings and JaypieEnvSecret instances)
3977
+ // Resolve secrets from mixed array (strings and JaypieSecret instances)
3761
3978
  // Use Stack.of(this) to ensure secrets are shared at stack level across all constructs
3762
3979
  const secrets = resolveSecrets(cdk.Stack.of(this), props?.secrets);
3763
3980
  // Process secrets environment variables
@@ -3765,7 +3982,7 @@ class JaypieNextJs extends constructs.Construct {
3765
3982
  ...acc,
3766
3983
  [`SECRET_${key}`]: secret.secretName,
3767
3984
  }), {});
3768
- // Process JaypieEnvSecret array
3985
+ // Process JaypieSecret array
3769
3986
  const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
3770
3987
  if (secret.envKey) {
3771
3988
  return {
@@ -3840,7 +4057,7 @@ class JaypieNextJs extends constructs.Construct {
3840
4057
  Object.values(envSecrets).forEach((secret) => {
3841
4058
  secret.grantRead(nextjs.serverFunction.lambdaFunction);
3842
4059
  });
3843
- // Grant read permissions for JaypieEnvSecrets
4060
+ // Grant read permissions for JaypieSecrets
3844
4061
  secrets.forEach((secret) => {
3845
4062
  secret.grantRead(nextjs.serverFunction.lambdaFunction);
3846
4063
  });
@@ -4709,6 +4926,8 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
4709
4926
  }
4710
4927
  else {
4711
4928
  const { managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES, rateLimitPerIp = DEFAULT_RATE_LIMIT, } = wafConfig;
4929
+ // Fail synth on rule names AWS WAF would silently ignore (#362)
4930
+ assertValidWafRuleNames({ managedRuleOverrides });
4712
4931
  let priority = 0;
4713
4932
  const rules = [];
4714
4933
  for (const ruleName of managedRules) {
@@ -5382,6 +5601,7 @@ class JaypieWebSocketTable extends constructs.Construct {
5382
5601
  }
5383
5602
  }
5384
5603
 
5604
+ exports.AWS_MANAGED_RULE_GROUPS = AWS_MANAGED_RULE_GROUPS;
5385
5605
  exports.CDK = CDK$2;
5386
5606
  exports.JaypieAccountLoggingBucket = JaypieAccountLoggingBucket;
5387
5607
  exports.JaypieApiGateway = JaypieApiGateway;
@@ -5407,6 +5627,7 @@ exports.JaypieNextJs = JaypieNextJs;
5407
5627
  exports.JaypieOpenAiSecret = JaypieOpenAiSecret;
5408
5628
  exports.JaypieOrganizationTrail = JaypieOrganizationTrail;
5409
5629
  exports.JaypieQueuedLambda = JaypieQueuedLambda;
5630
+ exports.JaypieSecret = JaypieSecret;
5410
5631
  exports.JaypieSsoPermissions = JaypieSsoPermissions;
5411
5632
  exports.JaypieSsoSyncApplication = JaypieSsoSyncApplication;
5412
5633
  exports.JaypieStack = JaypieStack;
@@ -5417,6 +5638,7 @@ exports.JaypieWebSocket = JaypieWebSocket;
5417
5638
  exports.JaypieWebSocketLambda = JaypieWebSocketLambda;
5418
5639
  exports.JaypieWebSocketTable = JaypieWebSocketTable;
5419
5640
  exports.addDatadogLayers = addDatadogLayers;
5641
+ exports.assertValidWafRuleNames = assertValidWafRuleNames;
5420
5642
  exports.clearAllCertificateCaches = clearAllCertificateCaches;
5421
5643
  exports.clearAllSecretsCaches = clearAllSecretsCaches;
5422
5644
  exports.clearCertificateCache = clearCertificateCache;