@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.
- package/dist/cjs/JaypieEnvSecret.d.ts +10 -35
- package/dist/cjs/JaypieLambda.d.ts +3 -2
- package/dist/cjs/JaypieNextJs.d.ts +2 -2
- package/dist/cjs/JaypieSecret.d.ts +59 -0
- package/dist/cjs/__tests__/JaypieSecret.spec.d.ts +1 -0
- package/dist/cjs/helpers/index.d.ts +1 -0
- package/dist/cjs/helpers/resolveSecrets.d.ts +10 -10
- package/dist/cjs/helpers/wafManagedRuleNames.d.ts +33 -0
- package/dist/cjs/index.cjs +326 -104
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/esm/JaypieEnvSecret.d.ts +10 -35
- package/dist/esm/JaypieLambda.d.ts +3 -2
- package/dist/esm/JaypieNextJs.d.ts +2 -2
- package/dist/esm/JaypieSecret.d.ts +59 -0
- package/dist/esm/__tests__/JaypieSecret.spec.d.ts +1 -0
- package/dist/esm/helpers/index.d.ts +1 -0
- package/dist/esm/helpers/resolveSecrets.d.ts +10 -10
- package/dist/esm/helpers/wafManagedRuleNames.d.ts +33 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +325 -106
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -973,115 +973,67 @@ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, } = {}) => {
|
|
|
973
973
|
return resolvedParamsAndSecrets;
|
|
974
974
|
};
|
|
975
975
|
|
|
976
|
-
|
|
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 ?
|
|
989
|
+
const id = treatAsEnvKey ? `${prefix}${idOrEnvKey}` : idOrEnvKey;
|
|
1019
990
|
super(scope, id);
|
|
1020
|
-
const
|
|
1021
|
-
const envKey = treatAsEnvKey ? idOrEnvKey : envKeyProp;
|
|
991
|
+
const envKey = treatAsEnvKey ? idOrEnvKey : props?.envKey;
|
|
1022
992
|
this._envKey = envKey;
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
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(`
|
|
1013
|
+
throw new errors.ConfigurationError(`JaypieSecret(${id}): envKey "${envKey}" is empty in process.env and no value or generateSecretString was provided`);
|
|
1041
1014
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
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
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
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
|
|
1245
|
+
* Resolves secrets input to an array of JaypieSecret instances.
|
|
1183
1246
|
*
|
|
1184
|
-
* When an item is already a JaypieEnvSecret, it's
|
|
1185
|
-
* When an item is a string, a JaypieEnvSecret is created
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|