@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/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as cdk from 'aws-cdk-lib';
|
|
2
|
-
import { Tags, Stack,
|
|
2
|
+
import { Tags, Stack, SecretValue, RemovalPolicy, Fn, CfnOutput, Duration, CfnStack } from 'aws-cdk-lib';
|
|
3
3
|
import * as s3 from 'aws-cdk-lib/aws-s3';
|
|
4
4
|
import { Bucket, StorageClass, BucketAccessControl, EventType } from 'aws-cdk-lib/aws-s3';
|
|
5
5
|
import { Construct } from 'constructs';
|
|
@@ -937,115 +937,67 @@ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, } = {}) => {
|
|
|
937
937
|
return resolvedParamsAndSecrets;
|
|
938
938
|
};
|
|
939
939
|
|
|
940
|
-
|
|
941
|
-
function checkEnvIsConsumer$1(env = process.env) {
|
|
942
|
-
return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
|
|
943
|
-
!!env.CDK_ENV_PERSONAL ||
|
|
944
|
-
/** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
|
|
945
|
-
/** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
|
|
946
|
-
}
|
|
947
|
-
function checkEnvIsProvider$1(env = process.env) {
|
|
948
|
-
return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
|
|
949
|
-
}
|
|
950
|
-
function cleanName$1(name) {
|
|
951
|
-
return name.replace(/[^a-zA-Z0-9:-]/g, "");
|
|
952
|
-
}
|
|
953
|
-
function exportEnvName$1(name, env = process.env, consumer = false) {
|
|
954
|
-
let rawName;
|
|
955
|
-
if (checkEnvIsProvider$1(env)) {
|
|
956
|
-
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
957
|
-
// Clean the entire name to only allow alphanumeric, colons, and hyphens
|
|
958
|
-
return cleanName$1(rawName);
|
|
959
|
-
}
|
|
960
|
-
else {
|
|
961
|
-
if (consumer || checkEnvIsConsumer$1(env)) {
|
|
962
|
-
rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
|
|
963
|
-
}
|
|
964
|
-
else {
|
|
965
|
-
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
return cleanName$1(rawName);
|
|
969
|
-
}
|
|
970
|
-
class JaypieEnvSecret extends Construct {
|
|
940
|
+
class JaypieSecret extends Construct {
|
|
971
941
|
constructor(scope, idOrEnvKey, props) {
|
|
972
942
|
// Shorthand detection: treat idOrEnvKey as envKey when envKey prop is
|
|
973
943
|
// not set and idOrEnvKey either looks like a SCREAMING_SNAKE_CASE env
|
|
974
944
|
// var name or is already present in process.env. Convention-based
|
|
975
945
|
// detection ensures missing env vars still go through envKey validation
|
|
976
946
|
// instead of silently creating an empty secret.
|
|
947
|
+
const prefix = new.target.shorthandPrefix;
|
|
977
948
|
const looksLikeEnvKey = /^[A-Z][A-Z0-9_]*$/.test(idOrEnvKey);
|
|
978
949
|
const treatAsEnvKey = (!props || props.envKey === undefined) &&
|
|
979
950
|
(looksLikeEnvKey ||
|
|
980
951
|
(typeof process.env[idOrEnvKey] === "string" &&
|
|
981
952
|
process.env[idOrEnvKey] !== ""));
|
|
982
|
-
const id = treatAsEnvKey ?
|
|
953
|
+
const id = treatAsEnvKey ? `${prefix}${idOrEnvKey}` : idOrEnvKey;
|
|
983
954
|
super(scope, id);
|
|
984
|
-
const
|
|
985
|
-
const envKey = treatAsEnvKey ? idOrEnvKey : envKeyProp;
|
|
955
|
+
const envKey = treatAsEnvKey ? idOrEnvKey : props?.envKey;
|
|
986
956
|
this._envKey = envKey;
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
957
|
+
this._secret = this.buildSecret({
|
|
958
|
+
envKey,
|
|
959
|
+
id,
|
|
960
|
+
props: props || {},
|
|
961
|
+
treatAsEnvKey,
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Builds the underlying secret. The base implementation always creates a new
|
|
966
|
+
* Secrets Manager secret from an envKey value, an explicit value, or a
|
|
967
|
+
* generated string. Subclasses may override to import an existing secret or
|
|
968
|
+
* emit cross-stack outputs.
|
|
969
|
+
*/
|
|
970
|
+
buildSecret(context) {
|
|
971
|
+
const { envKey, id, props } = context;
|
|
972
|
+
const { generateSecretString, removalPolicy, roleTag, vendorTag, value } = props;
|
|
973
|
+
if (envKey &&
|
|
1001
974
|
!process.env[envKey] &&
|
|
1002
975
|
value === undefined &&
|
|
1003
976
|
!generateSecretString) {
|
|
1004
|
-
throw new ConfigurationError(`
|
|
977
|
+
throw new ConfigurationError(`JaypieSecret(${id}): envKey "${envKey}" is empty in process.env and no value or generateSecretString was provided`);
|
|
1005
978
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
979
|
+
const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
|
|
980
|
+
const secret = new secretsmanager.Secret(this, id, {
|
|
981
|
+
generateSecretString,
|
|
982
|
+
secretStringValue: !generateSecretString && secretValue
|
|
983
|
+
? SecretValue.unsafePlainText(secretValue)
|
|
984
|
+
: undefined,
|
|
985
|
+
});
|
|
986
|
+
if (removalPolicy !== undefined) {
|
|
987
|
+
const policy = typeof removalPolicy === "boolean"
|
|
988
|
+
? removalPolicy
|
|
989
|
+
? RemovalPolicy.RETAIN
|
|
990
|
+
: RemovalPolicy.DESTROY
|
|
991
|
+
: removalPolicy;
|
|
992
|
+
secret.applyRemovalPolicy(policy);
|
|
1013
993
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
? SecretValue.unsafePlainText(secretValue)
|
|
1020
|
-
: undefined,
|
|
1021
|
-
};
|
|
1022
|
-
this._secret = new secretsmanager.Secret(this, id, secretProps);
|
|
1023
|
-
if (removalPolicy !== undefined) {
|
|
1024
|
-
const policy = typeof removalPolicy === "boolean"
|
|
1025
|
-
? removalPolicy
|
|
1026
|
-
? RemovalPolicy.RETAIN
|
|
1027
|
-
: RemovalPolicy.DESTROY
|
|
1028
|
-
: removalPolicy;
|
|
1029
|
-
this._secret.applyRemovalPolicy(policy);
|
|
1030
|
-
}
|
|
1031
|
-
if (roleTag) {
|
|
1032
|
-
Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
|
|
1033
|
-
}
|
|
1034
|
-
if (vendorTag) {
|
|
1035
|
-
Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
1036
|
-
}
|
|
1037
|
-
if (provider) {
|
|
1038
|
-
new CfnOutput(this, `ProvidedName`, {
|
|
1039
|
-
value: this._secret.secretName,
|
|
1040
|
-
exportName,
|
|
1041
|
-
});
|
|
1042
|
-
}
|
|
1043
|
-
else {
|
|
1044
|
-
new CfnOutput(this, `CreatedName`, {
|
|
1045
|
-
value: this._secret.secretName,
|
|
1046
|
-
});
|
|
1047
|
-
}
|
|
994
|
+
if (roleTag) {
|
|
995
|
+
Tags.of(secret).add(CDK$2.TAG.ROLE, roleTag);
|
|
996
|
+
}
|
|
997
|
+
if (vendorTag) {
|
|
998
|
+
Tags.of(secret).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
1048
999
|
}
|
|
1000
|
+
return secret;
|
|
1049
1001
|
}
|
|
1050
1002
|
// IResource implementation
|
|
1051
1003
|
get stack() {
|
|
@@ -1107,6 +1059,117 @@ class JaypieEnvSecret extends Construct {
|
|
|
1107
1059
|
return this._envKey;
|
|
1108
1060
|
}
|
|
1109
1061
|
}
|
|
1062
|
+
// Construct id prefix used when an envKey is detected via shorthand.
|
|
1063
|
+
// Subclasses override this to preserve their own naming conventions.
|
|
1064
|
+
JaypieSecret.shorthandPrefix = "Secret_";
|
|
1065
|
+
|
|
1066
|
+
// It is a consumer if the environment is ephemeral
|
|
1067
|
+
function checkEnvIsConsumer$1(env = process.env) {
|
|
1068
|
+
return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
|
|
1069
|
+
!!env.CDK_ENV_PERSONAL ||
|
|
1070
|
+
/** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
|
|
1071
|
+
/** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
|
|
1072
|
+
}
|
|
1073
|
+
function checkEnvIsProvider$1(env = process.env) {
|
|
1074
|
+
return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
|
|
1075
|
+
}
|
|
1076
|
+
function cleanName$1(name) {
|
|
1077
|
+
return name.replace(/[^a-zA-Z0-9:-]/g, "");
|
|
1078
|
+
}
|
|
1079
|
+
function exportEnvName$1(name, env = process.env, consumer = false) {
|
|
1080
|
+
let rawName;
|
|
1081
|
+
if (checkEnvIsProvider$1(env)) {
|
|
1082
|
+
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
1083
|
+
// Clean the entire name to only allow alphanumeric, colons, and hyphens
|
|
1084
|
+
return cleanName$1(rawName);
|
|
1085
|
+
}
|
|
1086
|
+
else {
|
|
1087
|
+
if (consumer || checkEnvIsConsumer$1(env)) {
|
|
1088
|
+
rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
return cleanName$1(rawName);
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* @deprecated Use {@link JaypieSecret}. JaypieEnvSecret layers an
|
|
1098
|
+
* environment-driven provider/consumer cross-stack pattern on top of
|
|
1099
|
+
* JaypieSecret and will be removed in 2.0.
|
|
1100
|
+
*/
|
|
1101
|
+
class JaypieEnvSecret extends JaypieSecret {
|
|
1102
|
+
constructor(scope, idOrEnvKey, props) {
|
|
1103
|
+
super(scope, idOrEnvKey, props);
|
|
1104
|
+
}
|
|
1105
|
+
buildSecret(context) {
|
|
1106
|
+
const { envKey, id, treatAsEnvKey } = context;
|
|
1107
|
+
const props = context.props;
|
|
1108
|
+
const { consumer = checkEnvIsConsumer$1(), export: exportParam, generateSecretString, provider = checkEnvIsProvider$1(), removalPolicy, roleTag, vendorTag, value, } = props;
|
|
1109
|
+
let exportName;
|
|
1110
|
+
if (!exportParam) {
|
|
1111
|
+
// When shorthand detection is active, use the full construct id (which
|
|
1112
|
+
// includes the "EnvSecret_" prefix) so the export name matches what was
|
|
1113
|
+
// produced by earlier versions of this construct. Using the raw envKey
|
|
1114
|
+
// here produces a shorter name that breaks existing cross-stack imports.
|
|
1115
|
+
const exportSource = treatAsEnvKey ? id : envKey || id;
|
|
1116
|
+
exportName = exportEnvName$1(exportSource, process.env, consumer);
|
|
1117
|
+
}
|
|
1118
|
+
else {
|
|
1119
|
+
exportName = cleanName$1(exportParam);
|
|
1120
|
+
}
|
|
1121
|
+
if (!consumer &&
|
|
1122
|
+
envKey &&
|
|
1123
|
+
!process.env[envKey] &&
|
|
1124
|
+
value === undefined &&
|
|
1125
|
+
!generateSecretString) {
|
|
1126
|
+
throw new ConfigurationError(`JaypieEnvSecret(${id}): envKey "${envKey}" is empty in process.env and no value or generateSecretString was provided`);
|
|
1127
|
+
}
|
|
1128
|
+
if (consumer) {
|
|
1129
|
+
const secretName = Fn.importValue(exportName);
|
|
1130
|
+
const secret = secretsmanager.Secret.fromSecretNameV2(this, id, secretName);
|
|
1131
|
+
// Add CfnOutput for consumer secrets
|
|
1132
|
+
new CfnOutput(this, `ConsumedName`, {
|
|
1133
|
+
value: secret.secretName,
|
|
1134
|
+
});
|
|
1135
|
+
return secret;
|
|
1136
|
+
}
|
|
1137
|
+
const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
|
|
1138
|
+
const secret = new secretsmanager.Secret(this, id, {
|
|
1139
|
+
generateSecretString,
|
|
1140
|
+
secretStringValue: !generateSecretString && secretValue
|
|
1141
|
+
? SecretValue.unsafePlainText(secretValue)
|
|
1142
|
+
: undefined,
|
|
1143
|
+
});
|
|
1144
|
+
if (removalPolicy !== undefined) {
|
|
1145
|
+
const policy = typeof removalPolicy === "boolean"
|
|
1146
|
+
? removalPolicy
|
|
1147
|
+
? RemovalPolicy.RETAIN
|
|
1148
|
+
: RemovalPolicy.DESTROY
|
|
1149
|
+
: removalPolicy;
|
|
1150
|
+
secret.applyRemovalPolicy(policy);
|
|
1151
|
+
}
|
|
1152
|
+
if (roleTag) {
|
|
1153
|
+
Tags.of(secret).add(CDK$2.TAG.ROLE, roleTag);
|
|
1154
|
+
}
|
|
1155
|
+
if (vendorTag) {
|
|
1156
|
+
Tags.of(secret).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
1157
|
+
}
|
|
1158
|
+
if (provider) {
|
|
1159
|
+
new CfnOutput(this, `ProvidedName`, {
|
|
1160
|
+
value: secret.secretName,
|
|
1161
|
+
exportName,
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
new CfnOutput(this, `CreatedName`, {
|
|
1166
|
+
value: secret.secretName,
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
return secret;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
JaypieEnvSecret.shorthandPrefix = "EnvSecret_";
|
|
1110
1173
|
|
|
1111
1174
|
/**
|
|
1112
1175
|
* Cache for secrets by scope to avoid creating duplicates.
|
|
@@ -1143,11 +1206,11 @@ function getOrCreateSecret(scope, envKey, props) {
|
|
|
1143
1206
|
return secret;
|
|
1144
1207
|
}
|
|
1145
1208
|
/**
|
|
1146
|
-
* Resolves secrets input to an array of
|
|
1209
|
+
* Resolves secrets input to an array of JaypieSecret instances.
|
|
1147
1210
|
*
|
|
1148
|
-
* When an item is already a JaypieEnvSecret, it's
|
|
1149
|
-
* When an item is a string, a JaypieEnvSecret is created
|
|
1150
|
-
* with the string as the envKey.
|
|
1211
|
+
* When an item is already a JaypieSecret (including a JaypieEnvSecret), it's
|
|
1212
|
+
* passed through as-is. When an item is a string, a JaypieEnvSecret is created
|
|
1213
|
+
* (or reused from cache) with the string as the envKey.
|
|
1151
1214
|
*
|
|
1152
1215
|
* Secrets are cached per scope to avoid creating duplicate secrets when
|
|
1153
1216
|
* multiple constructs in the same scope reference the same secret.
|
|
@@ -1183,7 +1246,7 @@ function resolveSecrets(scope, secrets) {
|
|
|
1183
1246
|
if (typeof item === "string") {
|
|
1184
1247
|
return getOrCreateSecret(scope, item);
|
|
1185
1248
|
}
|
|
1186
|
-
// Already a JaypieEnvSecret instance
|
|
1249
|
+
// Already a JaypieSecret (or JaypieEnvSecret) instance
|
|
1187
1250
|
return item;
|
|
1188
1251
|
});
|
|
1189
1252
|
}
|
|
@@ -1204,6 +1267,148 @@ function clearAllSecretsCaches() {
|
|
|
1204
1267
|
// between test runs. For testing, use clearSecretsCache(scope) instead.
|
|
1205
1268
|
}
|
|
1206
1269
|
|
|
1270
|
+
/**
|
|
1271
|
+
* Canonical sub-rule names for each AWS managed rule group, as published in the
|
|
1272
|
+
* AWS WAF developer guide. Used to validate `waf.allow` and
|
|
1273
|
+
* `waf.managedRuleOverrides` rule names at synth time — AWS WAF matches
|
|
1274
|
+
* `RuleActionOverride` on the exact rule *name* and silently ignores names that
|
|
1275
|
+
* match no rule, so a typo or a label/name casing mismatch (e.g. the label
|
|
1276
|
+
* `…:NoUserAgent_Header` vs the rule name `NoUserAgent_HEADER`) becomes an
|
|
1277
|
+
* undiagnosable no-op.
|
|
1278
|
+
*
|
|
1279
|
+
* Groups absent from this map (custom rule groups, or AWS groups not yet
|
|
1280
|
+
* mirrored here) are not validated.
|
|
1281
|
+
*
|
|
1282
|
+
* @see https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html
|
|
1283
|
+
*/
|
|
1284
|
+
const AWS_MANAGED_RULE_GROUPS = {
|
|
1285
|
+
AWSManagedRulesAdminProtectionRuleSet: ["AdminProtection_URIPATH"],
|
|
1286
|
+
AWSManagedRulesAmazonIpReputationList: [
|
|
1287
|
+
"AWSManagedIPDDoSList",
|
|
1288
|
+
"AWSManagedIPReputationList",
|
|
1289
|
+
"AWSManagedReconnaissanceList",
|
|
1290
|
+
],
|
|
1291
|
+
AWSManagedRulesAnonymousIpList: ["AnonymousIPList", "HostingProviderIPList"],
|
|
1292
|
+
AWSManagedRulesCommonRuleSet: [
|
|
1293
|
+
"CrossSiteScripting_BODY",
|
|
1294
|
+
"CrossSiteScripting_COOKIE",
|
|
1295
|
+
"CrossSiteScripting_QUERYARGUMENTS",
|
|
1296
|
+
"CrossSiteScripting_URIPATH",
|
|
1297
|
+
"EC2MetaDataSSRF_BODY",
|
|
1298
|
+
"EC2MetaDataSSRF_COOKIE",
|
|
1299
|
+
"EC2MetaDataSSRF_QUERYARGUMENTS",
|
|
1300
|
+
"EC2MetaDataSSRF_URIPATH",
|
|
1301
|
+
"GenericLFI_BODY",
|
|
1302
|
+
"GenericLFI_QUERYARGUMENTS",
|
|
1303
|
+
"GenericLFI_URIPATH",
|
|
1304
|
+
"GenericRFI_BODY",
|
|
1305
|
+
"GenericRFI_QUERYARGUMENTS",
|
|
1306
|
+
"GenericRFI_URIPATH",
|
|
1307
|
+
"NoUserAgent_HEADER",
|
|
1308
|
+
"RestrictedExtensions_QUERYARGUMENTS",
|
|
1309
|
+
"RestrictedExtensions_URIPATH",
|
|
1310
|
+
"SizeRestrictions_BODY",
|
|
1311
|
+
"SizeRestrictions_Cookie_HEADER",
|
|
1312
|
+
"SizeRestrictions_QUERYSTRING",
|
|
1313
|
+
"SizeRestrictions_URIPATH",
|
|
1314
|
+
"UserAgent_BadBots_HEADER",
|
|
1315
|
+
],
|
|
1316
|
+
AWSManagedRulesKnownBadInputsRuleSet: [
|
|
1317
|
+
"ExploitablePaths_URIPATH",
|
|
1318
|
+
"Host_localhost_HEADER",
|
|
1319
|
+
"JavaDeserializationRCE_BODY",
|
|
1320
|
+
"JavaDeserializationRCE_HEADER",
|
|
1321
|
+
"JavaDeserializationRCE_QUERYSTRING",
|
|
1322
|
+
"JavaDeserializationRCE_URIPATH",
|
|
1323
|
+
"Log4JRCE_BODY",
|
|
1324
|
+
"Log4JRCE_HEADER",
|
|
1325
|
+
"Log4JRCE_QUERYSTRING",
|
|
1326
|
+
"Log4JRCE_URIPATH",
|
|
1327
|
+
"PROPFIND_METHOD",
|
|
1328
|
+
"ReactJSRCE_BODY",
|
|
1329
|
+
],
|
|
1330
|
+
AWSManagedRulesLinuxRuleSet: ["LFI_HEADER", "LFI_QUERYSTRING", "LFI_URIPATH"],
|
|
1331
|
+
AWSManagedRulesPHPRuleSet: [
|
|
1332
|
+
"PHPHighRiskMethodsVariables_BODY",
|
|
1333
|
+
"PHPHighRiskMethodsVariables_HEADER",
|
|
1334
|
+
"PHPHighRiskMethodsVariables_QUERYSTRING",
|
|
1335
|
+
"PHPHighRiskMethodsVariables_URIPATH",
|
|
1336
|
+
],
|
|
1337
|
+
AWSManagedRulesSQLiRuleSet: [
|
|
1338
|
+
"SQLiExtendedPatterns_BODY",
|
|
1339
|
+
"SQLiExtendedPatterns_HEADER",
|
|
1340
|
+
"SQLiExtendedPatterns_QUERYARGUMENTS",
|
|
1341
|
+
"SQLiExtendedPatterns_URIPATH",
|
|
1342
|
+
"SQLi_BODY",
|
|
1343
|
+
"SQLi_COOKIE",
|
|
1344
|
+
"SQLi_QUERYARGUMENTS",
|
|
1345
|
+
"SQLi_URIPATH",
|
|
1346
|
+
],
|
|
1347
|
+
AWSManagedRulesUnixRuleSet: [
|
|
1348
|
+
"UNIXShellCommandsVariables_BODY",
|
|
1349
|
+
"UNIXShellCommandsVariables_HEADER",
|
|
1350
|
+
"UNIXShellCommandsVariables_QUERYSTRING",
|
|
1351
|
+
],
|
|
1352
|
+
AWSManagedRulesWindowsRuleSet: [
|
|
1353
|
+
"PowerShellCommands_BODY",
|
|
1354
|
+
"PowerShellCommands_COOKIE",
|
|
1355
|
+
"PowerShellCommands_QUERYARGUMENTS",
|
|
1356
|
+
"WindowsShellCommands_BODY",
|
|
1357
|
+
"WindowsShellCommands_HEADER",
|
|
1358
|
+
"WindowsShellCommands_QUERYARGUMENTS",
|
|
1359
|
+
"WindowsShellCommands_QUERYSTRING",
|
|
1360
|
+
"WindowsShellCommands_URIPATH",
|
|
1361
|
+
],
|
|
1362
|
+
AWSManagedRulesWordPressRuleSet: [
|
|
1363
|
+
"WordPressExploitableCommands_QUERYSTRING",
|
|
1364
|
+
"WordPressExploitablePaths_URIPATH",
|
|
1365
|
+
],
|
|
1366
|
+
};
|
|
1367
|
+
/**
|
|
1368
|
+
* Throw a ConfigurationError if any `waf.allow` or `waf.managedRuleOverrides`
|
|
1369
|
+
* rule name does not exist in its AWS managed rule group. Groups not present in
|
|
1370
|
+
* AWS_MANAGED_RULE_GROUPS (custom groups) are skipped. A name that matches no
|
|
1371
|
+
* rule would otherwise be silently ignored by AWS WAF.
|
|
1372
|
+
*/
|
|
1373
|
+
function assertValidWafRuleNames({ allow, managedRuleOverrides, } = {}) {
|
|
1374
|
+
// Collect (group → referenced rule name) pairs from both inputs.
|
|
1375
|
+
const references = [];
|
|
1376
|
+
if (managedRuleOverrides) {
|
|
1377
|
+
for (const [group, overrides] of Object.entries(managedRuleOverrides)) {
|
|
1378
|
+
for (const override of overrides ?? []) {
|
|
1379
|
+
if (override?.name)
|
|
1380
|
+
references.push({ group, ruleName: override.name });
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
const allowEntries = allow ? (Array.isArray(allow) ? allow : [allow]) : [];
|
|
1385
|
+
for (const entry of allowEntries) {
|
|
1386
|
+
for (const key of Object.keys(entry)) {
|
|
1387
|
+
if (key === "path")
|
|
1388
|
+
continue;
|
|
1389
|
+
const raw = entry[key];
|
|
1390
|
+
if (raw == null)
|
|
1391
|
+
continue;
|
|
1392
|
+
const ruleNames = Array.isArray(raw) ? raw : [raw];
|
|
1393
|
+
for (const ruleName of ruleNames) {
|
|
1394
|
+
references.push({ group: key, ruleName });
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
for (const { group, ruleName } of references) {
|
|
1399
|
+
const validNames = AWS_MANAGED_RULE_GROUPS[group];
|
|
1400
|
+
if (!validNames)
|
|
1401
|
+
continue; // Unknown/custom group — cannot validate
|
|
1402
|
+
if (!validNames.includes(ruleName)) {
|
|
1403
|
+
throw new ConfigurationError(`WAF rule "${ruleName}" is not a rule in ${group}. AWS WAF matches ` +
|
|
1404
|
+
`RuleActionOverrides on the exact rule name and silently ignores ` +
|
|
1405
|
+
`unmatched names (note the label/name casing trap, e.g. ` +
|
|
1406
|
+
`"NoUserAgent_HEADER" not "NoUserAgent_Header"). Valid rule names: ` +
|
|
1407
|
+
`${validNames.join(", ")}.`);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1207
1412
|
class JaypieApiGateway extends Construct {
|
|
1208
1413
|
constructor(scope, id, props) {
|
|
1209
1414
|
super(scope, id);
|
|
@@ -1393,12 +1598,12 @@ class JaypieLambda extends Construct {
|
|
|
1393
1598
|
super(scope, id);
|
|
1394
1599
|
const { allowAllOutbound, allowPublicSubnet, architecture = lambda.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.Runtime("nodejs24.x", lambda.RuntimeFamily.NODEJS, {
|
|
1395
1600
|
supportsInlineCode: true,
|
|
1396
|
-
}), runtimeManagementMode, secrets: secretsInput = [], securityGroups, timeout = Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
|
|
1601
|
+
}), runtimeManagementMode, secrets: secretsInput = [], securityGroups, serviceTag, timeout = Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
|
|
1397
1602
|
// Resolve environment from array or object syntax
|
|
1398
1603
|
const initialEnvironment = resolveEnvironment(environmentInput);
|
|
1399
1604
|
// Get base environment with defaults
|
|
1400
1605
|
const environment = jaypieLambdaEnv({ initialEnvironment });
|
|
1401
|
-
// Resolve secrets from mixed array (strings and
|
|
1606
|
+
// Resolve secrets from mixed array (strings and JaypieSecret instances)
|
|
1402
1607
|
// Use Stack.of(this) to ensure secrets are shared at stack level across all constructs
|
|
1403
1608
|
const secrets = resolveSecrets(Stack.of(this), secretsInput);
|
|
1404
1609
|
const codeAsset = typeof code === "string" ? lambda.Code.fromAsset(code) : code;
|
|
@@ -1409,7 +1614,7 @@ class JaypieLambda extends Construct {
|
|
|
1409
1614
|
...acc,
|
|
1410
1615
|
[`SECRET_${key}`]: secret.secretName,
|
|
1411
1616
|
}), {});
|
|
1412
|
-
// Process
|
|
1617
|
+
// Process JaypieSecret array
|
|
1413
1618
|
const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
|
|
1414
1619
|
if (secret.envKey) {
|
|
1415
1620
|
return {
|
|
@@ -1479,7 +1684,7 @@ class JaypieLambda extends Construct {
|
|
|
1479
1684
|
Object.values(envSecrets).forEach((secret) => {
|
|
1480
1685
|
secret.grantRead(this._lambda);
|
|
1481
1686
|
});
|
|
1482
|
-
// Grant read permissions for
|
|
1687
|
+
// Grant read permissions for JaypieSecrets
|
|
1483
1688
|
secrets.forEach((secret) => {
|
|
1484
1689
|
secret.grantRead(this._lambda);
|
|
1485
1690
|
});
|
|
@@ -1507,6 +1712,9 @@ class JaypieLambda extends Construct {
|
|
|
1507
1712
|
if (roleTag) {
|
|
1508
1713
|
Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
|
|
1509
1714
|
}
|
|
1715
|
+
if (serviceTag) {
|
|
1716
|
+
Tags.of(this._lambda).add(CDK$2.TAG.SERVICE, serviceTag);
|
|
1717
|
+
}
|
|
1510
1718
|
if (vendorTag) {
|
|
1511
1719
|
Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
1512
1720
|
}
|
|
@@ -1631,7 +1839,7 @@ class JaypieQueuedLambda extends Construct {
|
|
|
1631
1839
|
super(scope, id);
|
|
1632
1840
|
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.Runtime("nodejs24.x", lambda.RuntimeFamily.NODEJS, {
|
|
1633
1841
|
supportsInlineCode: true,
|
|
1634
|
-
}), runtimeManagementMode, secrets = [], securityGroups, tables = [], timeout = Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, visibilityTimeout = Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vpc, vpcSubnets, } = props;
|
|
1842
|
+
}), runtimeManagementMode, secrets = [], securityGroups, serviceTag, tables = [], timeout = Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, visibilityTimeout = Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vpc, vpcSubnets, } = props;
|
|
1635
1843
|
// Create SQS Queue
|
|
1636
1844
|
this._queue = new sqs.Queue(this, "Queue", {
|
|
1637
1845
|
fifo,
|
|
@@ -1642,6 +1850,9 @@ class JaypieQueuedLambda extends Construct {
|
|
|
1642
1850
|
if (roleTag) {
|
|
1643
1851
|
Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
|
|
1644
1852
|
}
|
|
1853
|
+
if (serviceTag) {
|
|
1854
|
+
Tags.of(this._queue).add(CDK$2.TAG.SERVICE, serviceTag);
|
|
1855
|
+
}
|
|
1645
1856
|
if (vendorTag) {
|
|
1646
1857
|
Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
1647
1858
|
}
|
|
@@ -1679,6 +1890,7 @@ class JaypieQueuedLambda extends Construct {
|
|
|
1679
1890
|
runtimeManagementMode,
|
|
1680
1891
|
secrets,
|
|
1681
1892
|
securityGroups,
|
|
1893
|
+
serviceTag,
|
|
1682
1894
|
tables,
|
|
1683
1895
|
timeout,
|
|
1684
1896
|
tracing,
|
|
@@ -1873,7 +2085,7 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
|
|
|
1873
2085
|
constructor(scope, id, props) {
|
|
1874
2086
|
props.fifo = false; // S3 event notifications are not supported for FIFO queues
|
|
1875
2087
|
super(scope, id, props);
|
|
1876
|
-
const { bucketName, roleTag, vendorTag, bucketOptions = {} } = props;
|
|
2088
|
+
const { bucketName, roleTag, serviceTag, vendorTag, bucketOptions = {}, } = props;
|
|
1877
2089
|
// Create S3 Bucket
|
|
1878
2090
|
this._bucket = new s3.Bucket(this, "Bucket", {
|
|
1879
2091
|
bucketName: bucketOptions.bucketName || bucketName,
|
|
@@ -1884,6 +2096,9 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
|
|
|
1884
2096
|
if (roleTag) {
|
|
1885
2097
|
Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
|
|
1886
2098
|
}
|
|
2099
|
+
if (serviceTag) {
|
|
2100
|
+
Tags.of(this._bucket).add(CDK$2.TAG.SERVICE, serviceTag);
|
|
2101
|
+
}
|
|
1887
2102
|
if (vendorTag) {
|
|
1888
2103
|
Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
1889
2104
|
}
|
|
@@ -2687,6 +2902,8 @@ class JaypieDistribution extends Construct {
|
|
|
2687
2902
|
else {
|
|
2688
2903
|
// Create new WebACL
|
|
2689
2904
|
const { allow, managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES$1, rateLimitPerIp = DEFAULT_RATE_LIMIT$1, } = wafConfig;
|
|
2905
|
+
// Fail synth on rule names AWS WAF would silently ignore (#362)
|
|
2906
|
+
assertValidWafRuleNames({ allow, managedRuleOverrides });
|
|
2690
2907
|
const allowEntries = allow
|
|
2691
2908
|
? Array.isArray(allow)
|
|
2692
2909
|
? allow
|
|
@@ -3721,7 +3938,7 @@ class JaypieNextJs extends Construct {
|
|
|
3721
3938
|
? path.join(process.cwd(), props.nextjsPath)
|
|
3722
3939
|
: props?.nextjsPath || path.join(process.cwd(), "..", "nextjs");
|
|
3723
3940
|
const paramsAndSecrets = resolveParamsAndSecrets();
|
|
3724
|
-
// Resolve secrets from mixed array (strings and
|
|
3941
|
+
// Resolve secrets from mixed array (strings and JaypieSecret instances)
|
|
3725
3942
|
// Use Stack.of(this) to ensure secrets are shared at stack level across all constructs
|
|
3726
3943
|
const secrets = resolveSecrets(Stack.of(this), props?.secrets);
|
|
3727
3944
|
// Process secrets environment variables
|
|
@@ -3729,7 +3946,7 @@ class JaypieNextJs extends Construct {
|
|
|
3729
3946
|
...acc,
|
|
3730
3947
|
[`SECRET_${key}`]: secret.secretName,
|
|
3731
3948
|
}), {});
|
|
3732
|
-
// Process
|
|
3949
|
+
// Process JaypieSecret array
|
|
3733
3950
|
const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
|
|
3734
3951
|
if (secret.envKey) {
|
|
3735
3952
|
return {
|
|
@@ -3804,7 +4021,7 @@ class JaypieNextJs extends Construct {
|
|
|
3804
4021
|
Object.values(envSecrets).forEach((secret) => {
|
|
3805
4022
|
secret.grantRead(nextjs.serverFunction.lambdaFunction);
|
|
3806
4023
|
});
|
|
3807
|
-
// Grant read permissions for
|
|
4024
|
+
// Grant read permissions for JaypieSecrets
|
|
3808
4025
|
secrets.forEach((secret) => {
|
|
3809
4026
|
secret.grantRead(nextjs.serverFunction.lambdaFunction);
|
|
3810
4027
|
});
|
|
@@ -4673,6 +4890,8 @@ class JaypieWebDeploymentBucket extends Construct {
|
|
|
4673
4890
|
}
|
|
4674
4891
|
else {
|
|
4675
4892
|
const { managedRuleOverrides, managedRuleScopeDowns, managedRules = DEFAULT_MANAGED_RULES, rateLimitPerIp = DEFAULT_RATE_LIMIT, } = wafConfig;
|
|
4893
|
+
// Fail synth on rule names AWS WAF would silently ignore (#362)
|
|
4894
|
+
assertValidWafRuleNames({ managedRuleOverrides });
|
|
4676
4895
|
let priority = 0;
|
|
4677
4896
|
const rules = [];
|
|
4678
4897
|
for (const ruleName of managedRules) {
|
|
@@ -5346,5 +5565,5 @@ class JaypieWebSocketTable extends Construct {
|
|
|
5346
5565
|
}
|
|
5347
5566
|
}
|
|
5348
5567
|
|
|
5349
|
-
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, 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 };
|
|
5568
|
+
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 };
|
|
5350
5569
|
//# sourceMappingURL=index.js.map
|