@aaa-sentinel/iac-cost 0.4.0 → 0.4.1
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/README.md +5 -5
- package/dist/cli.js +406 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -175,19 +175,19 @@ value from the plan: a variable is named (`DB_PASSWORD`), its value is not read.
|
|
|
175
175
|
|
|
176
176
|
| Cloud | Terraform resources | Rules |
|
|
177
177
|
| --- | --- | --- |
|
|
178
|
-
| AWS | `aws_security_group`, `aws_default_security_group`, `aws_security_group_rule`, `aws_vpc_security_group_ingress_rule` (+ `aws_ec2_managed_prefix_list`) | Admin ports (22/3389) open to the internet, directly or through a prefix list |
|
|
178
|
+
| AWS | `aws_security_group`, `aws_default_security_group`, `aws_security_group_rule`, `aws_vpc_security_group_ingress_rule` (+ `aws_ec2_managed_prefix_list`) | Admin ports (22/3389) open to the internet, directly or through a prefix list; database ports (SQL Server, Oracle, MySQL, PostgreSQL, Redis, Elasticsearch, MongoDB) open to the internet over TCP |
|
|
179
179
|
| AWS | `aws_s3_bucket` + `aws_s3_bucket_public_access_block`, `aws_s3_account_public_access_block`, `aws_s3_bucket_acl`, `aws_s3_bucket_policy` | Public bucket |
|
|
180
|
-
| AWS | `aws_db_instance`, `aws_rds_cluster_instance` (+ `aws_rds_cluster`) | Unencrypted storage |
|
|
180
|
+
| AWS | `aws_db_instance`, `aws_rds_cluster_instance` (+ `aws_rds_cluster`) | Unencrypted storage; publicly accessible |
|
|
181
181
|
| AWS | `aws_lambda_function` + `aws_lambda_function_url`, `aws_lambda_permission` | Invocable from the internet; secret-looking environment variable names |
|
|
182
182
|
| AWS | `aws_cloudtrail` | Trail stopped; management events excluded |
|
|
183
183
|
| AWS | `aws_kms_key` | Key rotation off |
|
|
184
184
|
| AWS | `aws_vpc`, `aws_default_vpc` + `aws_flow_log` (+ `aws_subnet`) | VPC without flow logs; flow logs that miss part of the traffic |
|
|
185
|
-
| Azure | `azurerm_network_security_group`, `azurerm_network_security_rule` | Admin ports open to the internet |
|
|
185
|
+
| Azure | `azurerm_network_security_group`, `azurerm_network_security_rule` | Admin ports open to the internet; database ports open to the internet over TCP |
|
|
186
186
|
| Azure | `azurerm_storage_account` | Public blobs allowed; weak TLS or HTTP |
|
|
187
187
|
| Azure | `azurerm_mssql_server` + `azurerm_mssql_firewall_rule` | Public network access open to 0.0.0.0–255.255.255.255; weak TLS |
|
|
188
188
|
| Azure | `azurerm_role_assignment` | Owner or User Access Administrator at any scope; Contributor at subscription scope (a Contributor at a management group or at `/` is not flagged, neither here nor in the live scan) |
|
|
189
|
-
| Google Cloud | `google_compute_firewall` | Admin ports open to the internet |
|
|
190
|
-
| Google Cloud | `google_sql_database_instance` | Authorized from 0.0.0.0/0; no CMEK |
|
|
189
|
+
| Google Cloud | `google_compute_firewall` | Admin ports open to the internet; database ports open to the internet over TCP |
|
|
190
|
+
| Google Cloud | `google_sql_database_instance` | Authorized from 0.0.0.0/0; no CMEK; unencrypted connections accepted (`ssl_mode`; not judged while the provider still has to compute it) |
|
|
191
191
|
| Google Cloud | `google_cloudfunctions_function`, `google_cloudfunctions2_function` | Secret-looking environment variable names |
|
|
192
192
|
| Google Cloud | `google_project_iam_member`, `_binding`, `_policy`; `google_service_account_key` | Primitive Owner/Editor role; user-managed key |
|
|
193
193
|
|
package/dist/cli.js
CHANGED
|
@@ -8153,6 +8153,8 @@ function sqlInstances(index) {
|
|
|
8153
8153
|
const builder = new ObservationBuilder(index, sql, "cloud_sql_instance", "gcp").cloudId("name");
|
|
8154
8154
|
const ip = ["settings", 0, "ip_configuration", 0];
|
|
8155
8155
|
builder.from("ipv4Enabled", index.get(sql, ...ip, "ipv4_enabled"), Boolean, true);
|
|
8156
|
+
builder.from("sslMode", index.get(sql, ...ip, "ssl_mode"), String, null);
|
|
8157
|
+
builder.from("requireSsl", index.get(sql, ...ip, "require_ssl"), Boolean, null);
|
|
8156
8158
|
const networks = index.get(sql, ...ip, "authorized_networks");
|
|
8157
8159
|
if (networks.state === "unknown") builder.gap("authorizedNetworks", "known_after_apply");
|
|
8158
8160
|
else {
|
|
@@ -8268,6 +8270,18 @@ var RULE_TITLES = {
|
|
|
8268
8270
|
SEC_AWS_NETWORK_ADMIN_PREFIX_LIST: "Admin ports open to the internet through a prefix list",
|
|
8269
8271
|
SEC_AWS_EXPOSURE_S3_PUBLICO: "Public S3 bucket",
|
|
8270
8272
|
SEC_AWS_EXPOSURE_LAMBDA_PUBLICA: "Lambda invocable from the internet",
|
|
8273
|
+
SEC_AZURE_ENCRYPTION_KEYVAULT_SIN_PURGA: "Key Vault without purge protection",
|
|
8274
|
+
SEC_AZURE_ENCRYPTION_SQL_SIN_TDE: "SQL database without TDE encryption",
|
|
8275
|
+
SEC_AZURE_LOGGING_VNET_SIN_FLOW_LOGS: "VNet without flow logs",
|
|
8276
|
+
SEC_GCP_ENCRYPTION_KMS_SIN_ROTACION: "Cloud KMS key without 90-day rotation",
|
|
8277
|
+
SEC_GCP_LOGGING_VPC_SIN_FLOW_LOGS: "VPC network with subnets without flow logs",
|
|
8278
|
+
SEC_AWS_NETWORK_BD_ABIERTO: "Database ports open to the internet",
|
|
8279
|
+
SEC_AZURE_NETWORK_BD_ABIERTO: "NSG with database ports open to the internet",
|
|
8280
|
+
SEC_GCP_NETWORK_BD_ABIERTO: "Firewall with database ports open to the internet",
|
|
8281
|
+
SEC_AWS_EXPOSURE_RDS_PUBLICA: "Publicly accessible RDS instance",
|
|
8282
|
+
SEC_GCP_ENCRYPTION_SQL_SIN_SSL: "Cloud SQL accepts unencrypted connections",
|
|
8283
|
+
SEC_AWS_IAM_CLAVE_ANTIGUA: "Active access key older than 90 days",
|
|
8284
|
+
SEC_AWS_IAM_POLITICA_CONTRASENAS: "Weak or missing IAM password policy",
|
|
8271
8285
|
SEC_AWS_ENCRYPTION_KMS_SIN_ROTACION: "KMS key without rotation",
|
|
8272
8286
|
SEC_AWS_ENCRYPTION_RDS_SIN_CIFRAR: "Unencrypted RDS",
|
|
8273
8287
|
SEC_AWS_LOGGING_TRAIL_PARADO: "CloudTrail stopped",
|
|
@@ -9540,8 +9554,6 @@ function publicVulnerable(input, provider) {
|
|
|
9540
9554
|
}
|
|
9541
9555
|
|
|
9542
9556
|
// ../../services/security/src/rules/aws/combos.ts
|
|
9543
|
-
var DIA_MS = 24 * 60 * 60 * 1e3;
|
|
9544
|
-
var OLD_ACCESS_KEY_DAYS = 90;
|
|
9545
9557
|
function lambdaRef(resourceId, region) {
|
|
9546
9558
|
const partes = resourceId.split(":");
|
|
9547
9559
|
if (partes[0] === "arn" && partes[2] === "lambda" && partes[5] === "function" && partes[3] && partes[6]) {
|
|
@@ -9580,38 +9592,23 @@ var AWS_SECURITY_COMBOS = [
|
|
|
9580
9592
|
id: "COMBO_AWS_PUBLIC_DB_UNENCRYPTED",
|
|
9581
9593
|
provider: "aws",
|
|
9582
9594
|
severity: "critical",
|
|
9583
|
-
rules: ["SEC_AWS_ENCRYPTION_RDS_SIN_CIFRAR"],
|
|
9584
|
-
detect: (input) => input
|
|
9585
|
-
const db = observationOf(input, "rds_instance", f.region, f.resourceId);
|
|
9586
|
-
if (db?.attributes["publiclyAccessible"] !== true) return [];
|
|
9587
|
-
return [{ region: f.region, resourceId: f.resourceId, resourceName: f.resourceName, findingIds: [f.id], evidence: { publiclyAccessible: true } }];
|
|
9588
|
-
})
|
|
9595
|
+
rules: ["SEC_AWS_ENCRYPTION_RDS_SIN_CIFRAR", "SEC_AWS_EXPOSURE_RDS_PUBLICA"],
|
|
9596
|
+
detect: (input) => sameResource(input, "SEC_AWS_ENCRYPTION_RDS_SIN_CIFRAR", "SEC_AWS_EXPOSURE_RDS_PUBLICA").map((m) => ({ ...m, evidence: { publiclyAccessible: true } }))
|
|
9589
9597
|
},
|
|
9590
9598
|
{
|
|
9591
9599
|
// Entra a la consola solo con contraseña y además tiene una clave de acceso vieja y activa: dos puertas sin segundo factor.
|
|
9592
9600
|
id: "COMBO_AWS_USER_NO_MFA_OLD_KEY",
|
|
9593
9601
|
provider: "aws",
|
|
9594
9602
|
severity: "critical",
|
|
9595
|
-
rules: ["SEC_AWS_IAM_USUARIO_SIN_MFA"],
|
|
9596
|
-
detect: (input) =>
|
|
9597
|
-
const
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
const
|
|
9601
|
-
return
|
|
9602
|
-
})
|
|
9603
|
-
|
|
9604
|
-
return [
|
|
9605
|
-
{
|
|
9606
|
-
region: f.region,
|
|
9607
|
-
resourceId: f.resourceId,
|
|
9608
|
-
resourceName: f.resourceName,
|
|
9609
|
-
findingIds: [f.id],
|
|
9610
|
-
// La fecha y no la edad: la edad la calcula quien la enseña, si no se quedaría congelada el día del escaneo.
|
|
9611
|
-
evidence: { oldActiveKeys: viejas.length, oldestKeyCreatedAt: viejas[0] }
|
|
9612
|
-
}
|
|
9613
|
-
];
|
|
9614
|
-
})
|
|
9603
|
+
rules: ["SEC_AWS_IAM_USUARIO_SIN_MFA", "SEC_AWS_IAM_CLAVE_ANTIGUA"],
|
|
9604
|
+
detect: (input) => {
|
|
9605
|
+
const viejas = findingsByResource(input.findings, "SEC_AWS_IAM_CLAVE_ANTIGUA");
|
|
9606
|
+
return sameResource(input, "SEC_AWS_IAM_USUARIO_SIN_MFA", "SEC_AWS_IAM_CLAVE_ANTIGUA").map((m) => {
|
|
9607
|
+
const claves = viejas.get(`${m.region}::${m.resourceId}`)?.evidence["oldActiveKeys"];
|
|
9608
|
+
const lista = Array.isArray(claves) ? claves : [];
|
|
9609
|
+
return { ...m, evidence: { oldActiveKeys: lista.length, oldestKeyCreatedAt: lista[0]?.["createdAt"] ?? null } };
|
|
9610
|
+
});
|
|
9611
|
+
}
|
|
9615
9612
|
},
|
|
9616
9613
|
{
|
|
9617
9614
|
// Alcanzable desde internet y con un CVE crítico o alto que ya tiene exploit público: el ataque está escrito.
|
|
@@ -10090,6 +10087,31 @@ var rdsClusterSnapshotPublico = {
|
|
|
10090
10087
|
],
|
|
10091
10088
|
evaluate: (observation) => snapshotPublicoEvaluate(observation, RDS_CHECKS.clusterSnapshotAttributes)
|
|
10092
10089
|
};
|
|
10090
|
+
function rdsPublicaEvaluate(observation) {
|
|
10091
|
+
const publica = observation.attributes.publiclyAccessible;
|
|
10092
|
+
if (typeof publica !== "boolean") {
|
|
10093
|
+
return { status: "unknown", check: RDS_CHECKS.instances, attribute: "publiclyAccessible" };
|
|
10094
|
+
}
|
|
10095
|
+
if (!publica) return { status: "ok" };
|
|
10096
|
+
return {
|
|
10097
|
+
status: "finding",
|
|
10098
|
+
evidence: {
|
|
10099
|
+
publiclyAccessible: true,
|
|
10100
|
+
engine: observation.attributes.engine ?? null,
|
|
10101
|
+
securityGroupIds: observation.attributes.securityGroupIds ?? null
|
|
10102
|
+
}
|
|
10103
|
+
};
|
|
10104
|
+
}
|
|
10105
|
+
var rdsPublica = {
|
|
10106
|
+
id: "SEC_AWS_EXPOSURE_RDS_PUBLICA",
|
|
10107
|
+
scope: "resource",
|
|
10108
|
+
category: "security_exposure",
|
|
10109
|
+
severity: "high",
|
|
10110
|
+
provider: "aws",
|
|
10111
|
+
kinds: ["rds_instance"],
|
|
10112
|
+
requires: [REGION_CHECKS.listRegions, RDS_CHECKS.instances],
|
|
10113
|
+
evaluate: rdsPublicaEvaluate
|
|
10114
|
+
};
|
|
10093
10115
|
|
|
10094
10116
|
// ../../services/security/src/rules/aws/iam.ts
|
|
10095
10117
|
function falta4(check, attribute) {
|
|
@@ -10143,6 +10165,74 @@ var usuarioSinMfa = {
|
|
|
10143
10165
|
requires: [IAM_CHECKS.listUsers, IAM_CHECKS.loginProfile, IAM_CHECKS.mfaDevices],
|
|
10144
10166
|
evaluate: usuarioSinMfaEvaluate
|
|
10145
10167
|
};
|
|
10168
|
+
var ACCESS_KEY_MAX_AGE_DAYS = 90;
|
|
10169
|
+
var DIA_MS = 864e5;
|
|
10170
|
+
function claveAntiguaEvaluate(observation, ahora = Date.now()) {
|
|
10171
|
+
const claves = observation.attributes.accessKeys;
|
|
10172
|
+
if (!Array.isArray(claves)) return falta4(IAM_CHECKS.accessKeys, "accessKeys");
|
|
10173
|
+
const viejas = [];
|
|
10174
|
+
let indecisa = false;
|
|
10175
|
+
for (const entrada of claves) {
|
|
10176
|
+
if (typeof entrada !== "object" || entrada === null) {
|
|
10177
|
+
indecisa = true;
|
|
10178
|
+
continue;
|
|
10179
|
+
}
|
|
10180
|
+
const clave = entrada;
|
|
10181
|
+
if (clave.active === false) continue;
|
|
10182
|
+
const creada = typeof clave.createdAt === "string" ? Date.parse(clave.createdAt) : Number.NaN;
|
|
10183
|
+
if (clave.active !== true || Number.isNaN(creada)) {
|
|
10184
|
+
indecisa = true;
|
|
10185
|
+
continue;
|
|
10186
|
+
}
|
|
10187
|
+
if (ahora - creada > ACCESS_KEY_MAX_AGE_DAYS * DIA_MS) viejas.push({ id: clave.id ?? null, createdAt: clave.createdAt });
|
|
10188
|
+
}
|
|
10189
|
+
if (viejas.length === 0) return indecisa ? falta4(IAM_CHECKS.accessKeys, "accessKeys") : { status: "ok" };
|
|
10190
|
+
viejas.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
10191
|
+
return { status: "finding", evidence: { oldActiveKeys: viejas, maxAgeDays: ACCESS_KEY_MAX_AGE_DAYS } };
|
|
10192
|
+
}
|
|
10193
|
+
var claveAntigua = {
|
|
10194
|
+
id: "SEC_AWS_IAM_CLAVE_ANTIGUA",
|
|
10195
|
+
scope: "resource",
|
|
10196
|
+
category: "security_iam",
|
|
10197
|
+
severity: "medium",
|
|
10198
|
+
provider: "aws",
|
|
10199
|
+
kinds: ["iam_user"],
|
|
10200
|
+
requires: [IAM_CHECKS.listUsers, IAM_CHECKS.accessKeys],
|
|
10201
|
+
evaluate: (observation) => claveAntiguaEvaluate(observation)
|
|
10202
|
+
};
|
|
10203
|
+
var PASSWORD_MIN_LENGTH = 14;
|
|
10204
|
+
var PASSWORD_REUSE_PREVENTION = 24;
|
|
10205
|
+
function politicaContrasenasEvaluate(observation) {
|
|
10206
|
+
const usuarios = observation.attributes.userCount;
|
|
10207
|
+
if (typeof usuarios !== "number") return falta4(IAM_CHECKS.accountSummary, "userCount");
|
|
10208
|
+
if (usuarios === 0) return { status: "ok" };
|
|
10209
|
+
if (!("passwordPolicy" in observation.attributes)) return falta4(IAM_CHECKS.passwordPolicy, "passwordPolicy");
|
|
10210
|
+
const politica = observation.attributes.passwordPolicy;
|
|
10211
|
+
if (politica === null) {
|
|
10212
|
+
return { status: "finding", evidence: { passwordPolicy: null, userCount: usuarios } };
|
|
10213
|
+
}
|
|
10214
|
+
if (typeof politica !== "object" || Array.isArray(politica)) return falta4(IAM_CHECKS.passwordPolicy, "passwordPolicy");
|
|
10215
|
+
const p = politica;
|
|
10216
|
+
const longitud = typeof p.minimumLength === "number" ? p.minimumLength : null;
|
|
10217
|
+
const reuso = typeof p.reusePrevention === "number" ? p.reusePrevention : null;
|
|
10218
|
+
const corta = longitud === null || longitud < PASSWORD_MIN_LENGTH;
|
|
10219
|
+
const reutilizable = reuso === null || reuso < PASSWORD_REUSE_PREVENTION;
|
|
10220
|
+
if (!corta && !reutilizable) return { status: "ok" };
|
|
10221
|
+
return {
|
|
10222
|
+
status: "finding",
|
|
10223
|
+
evidence: { minimumLength: longitud, reusePrevention: reuso, userCount: usuarios }
|
|
10224
|
+
};
|
|
10225
|
+
}
|
|
10226
|
+
var politicaContrasenas = {
|
|
10227
|
+
id: "SEC_AWS_IAM_POLITICA_CONTRASENAS",
|
|
10228
|
+
scope: "resource",
|
|
10229
|
+
category: "security_iam",
|
|
10230
|
+
severity: "low",
|
|
10231
|
+
provider: "aws",
|
|
10232
|
+
kinds: ["iam_account"],
|
|
10233
|
+
requires: [IAM_CHECKS.accountSummary, IAM_CHECKS.passwordPolicy],
|
|
10234
|
+
evaluate: politicaContrasenasEvaluate
|
|
10235
|
+
};
|
|
10146
10236
|
function exclusion(observation) {
|
|
10147
10237
|
const value = observation.attributes.excluded;
|
|
10148
10238
|
if (value === null) return { known: true, excluded: false };
|
|
@@ -10561,6 +10651,7 @@ var trailGestionIncompleta = {
|
|
|
10561
10651
|
|
|
10562
10652
|
// ../../services/security/src/collectors/internet.ts
|
|
10563
10653
|
var ADMIN_PORTS2 = [22, 3389];
|
|
10654
|
+
var DATABASE_PORTS = [1433, 1521, 2483, 2484, 3306, 5432, 6379, 9200, 9300, 27017, 27018, 27019];
|
|
10564
10655
|
var ETIQUETAS_INTERNET = /* @__PURE__ */ new Set(["*", "internet", "any", "/0"]);
|
|
10565
10656
|
function longitudPrefijo(cidr, maximo) {
|
|
10566
10657
|
const partes = cidr.split("/");
|
|
@@ -10601,13 +10692,21 @@ function protocolName(protocol) {
|
|
|
10601
10692
|
const p = protocol.trim().toLowerCase();
|
|
10602
10693
|
return POR_NUMERO2[p] ?? p;
|
|
10603
10694
|
}
|
|
10604
|
-
function
|
|
10695
|
+
function portsReached(ports, protocol, portRanges) {
|
|
10605
10696
|
const p = protocolName(protocol);
|
|
10606
10697
|
const todos = p === "*" || p === "any" || p === "all";
|
|
10607
|
-
if (todos) return [...
|
|
10698
|
+
if (todos) return [...ports];
|
|
10608
10699
|
if (p !== "tcp" && p !== "udp") return [];
|
|
10609
|
-
if (portRanges.length === 0) return [...
|
|
10610
|
-
return
|
|
10700
|
+
if (portRanges.length === 0) return [...ports];
|
|
10701
|
+
return ports.filter((puerto) => portRanges.some((rango) => cubrePuerto(rango, puerto)));
|
|
10702
|
+
}
|
|
10703
|
+
function adminPortsReached(protocol, portRanges) {
|
|
10704
|
+
return portsReached(ADMIN_PORTS2, protocol, portRanges);
|
|
10705
|
+
}
|
|
10706
|
+
function databasePortsReached(protocol, portRanges) {
|
|
10707
|
+
if (protocolName(protocol) === "udp") return [];
|
|
10708
|
+
if (adminPortsReached(protocol, portRanges).length > 0) return [];
|
|
10709
|
+
return portsReached(DATABASE_PORTS, protocol, portRanges);
|
|
10611
10710
|
}
|
|
10612
10711
|
function cadenas2(value) {
|
|
10613
10712
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
@@ -10843,6 +10942,67 @@ var adminAbiertoPorPrefixList = {
|
|
|
10843
10942
|
requires: [REGION_CHECKS.listRegions, EC2_CHECKS.securityGroups, EC2_CHECKS.prefixListEntries],
|
|
10844
10943
|
evaluate: prefixListEvaluate
|
|
10845
10944
|
};
|
|
10945
|
+
function puertosDeBaseDeDatos(regla) {
|
|
10946
|
+
if (typeof regla.protocol !== "string") return void 0;
|
|
10947
|
+
const protocolo = protocolName(regla.protocol);
|
|
10948
|
+
if (protocolo !== "tcp") return [];
|
|
10949
|
+
const desde = regla.fromPort;
|
|
10950
|
+
const hasta = regla.toPort;
|
|
10951
|
+
if (typeof desde !== "number" || typeof hasta !== "number") return void 0;
|
|
10952
|
+
if (ADMIN_PORTS2.some((puerto) => desde <= puerto && puerto <= hasta)) return [];
|
|
10953
|
+
return DATABASE_PORTS.filter((puerto) => desde <= puerto && puerto <= hasta);
|
|
10954
|
+
}
|
|
10955
|
+
function bdAbiertaEvaluate(observation) {
|
|
10956
|
+
const ingress = observation.attributes.ingressRules;
|
|
10957
|
+
if (!Array.isArray(ingress)) {
|
|
10958
|
+
return { status: "unknown", check: EC2_CHECKS.securityGroups, attribute: "ingressRules" };
|
|
10959
|
+
}
|
|
10960
|
+
const aperturas = [];
|
|
10961
|
+
const puertos = /* @__PURE__ */ new Set();
|
|
10962
|
+
let indecisa = false;
|
|
10963
|
+
for (const entrada of ingress) {
|
|
10964
|
+
if (typeof entrada !== "object" || entrada === null) {
|
|
10965
|
+
indecisa = true;
|
|
10966
|
+
continue;
|
|
10967
|
+
}
|
|
10968
|
+
const regla = entrada;
|
|
10969
|
+
const origenes = [...cadenas2(regla.cidrs), ...cadenas2(regla.ipv6Cidrs)];
|
|
10970
|
+
const abierto = anySourceOpensToInternet(origenes);
|
|
10971
|
+
if (abierto === void 0) {
|
|
10972
|
+
indecisa = true;
|
|
10973
|
+
continue;
|
|
10974
|
+
}
|
|
10975
|
+
if (!abierto) continue;
|
|
10976
|
+
const alcanzados = puertosDeBaseDeDatos(regla);
|
|
10977
|
+
if (alcanzados === void 0) {
|
|
10978
|
+
indecisa = true;
|
|
10979
|
+
continue;
|
|
10980
|
+
}
|
|
10981
|
+
if (alcanzados.length === 0) continue;
|
|
10982
|
+
for (const puerto of alcanzados) puertos.add(puerto);
|
|
10983
|
+
aperturas.push({
|
|
10984
|
+
protocol: regla.protocol,
|
|
10985
|
+
fromPort: regla.fromPort ?? null,
|
|
10986
|
+
toPort: regla.toPort ?? null,
|
|
10987
|
+
cidr: origenes.find((cidr) => sourceOpensToInternet(cidr) === true) ?? null
|
|
10988
|
+
});
|
|
10989
|
+
}
|
|
10990
|
+
if (aperturas.length > 0) {
|
|
10991
|
+
return { status: "finding", evidence: { ports: [...puertos].sort((a, b) => a - b), openings: aperturas } };
|
|
10992
|
+
}
|
|
10993
|
+
if (indecisa) return { status: "unknown", check: EC2_CHECKS.securityGroups, attribute: "ingressRules" };
|
|
10994
|
+
return { status: "ok" };
|
|
10995
|
+
}
|
|
10996
|
+
var bdAbiertaAInternet = {
|
|
10997
|
+
id: "SEC_AWS_NETWORK_BD_ABIERTO",
|
|
10998
|
+
scope: "resource",
|
|
10999
|
+
category: "security_network",
|
|
11000
|
+
severity: "high",
|
|
11001
|
+
provider: "aws",
|
|
11002
|
+
kinds: ["security_group"],
|
|
11003
|
+
requires: [REGION_CHECKS.listRegions, EC2_CHECKS.securityGroups],
|
|
11004
|
+
evaluate: bdAbiertaEvaluate
|
|
11005
|
+
};
|
|
10846
11006
|
|
|
10847
11007
|
// ../../services/security/src/rules/aws/monitoring.ts
|
|
10848
11008
|
var evento = (...nombres) => nombres.map((n) => `$.eventName=${n}`);
|
|
@@ -11174,17 +11334,21 @@ var secretoEnVariableDeEntorno = {
|
|
|
11174
11334
|
var AWS_SECURITY_RULES = [
|
|
11175
11335
|
adminAbiertoAInternet,
|
|
11176
11336
|
adminAbiertoPorPrefixList,
|
|
11337
|
+
bdAbiertaAInternet,
|
|
11177
11338
|
lightsailAdminAbierto,
|
|
11178
11339
|
nlbAdminAbierto,
|
|
11179
11340
|
bucketPublico,
|
|
11180
11341
|
ebsSnapshotPublico,
|
|
11181
11342
|
rdsSnapshotPublico,
|
|
11182
11343
|
rdsClusterSnapshotPublico,
|
|
11344
|
+
rdsPublica,
|
|
11183
11345
|
lambdaPublica,
|
|
11184
11346
|
kmsSinRotacion,
|
|
11185
11347
|
rdsSinCifrar,
|
|
11186
11348
|
rootSinMfa,
|
|
11187
11349
|
usuarioSinMfa,
|
|
11350
|
+
claveAntigua,
|
|
11351
|
+
politicaContrasenas,
|
|
11188
11352
|
adminEfectivo,
|
|
11189
11353
|
identidadSinUso,
|
|
11190
11354
|
claveSinUso,
|
|
@@ -11237,7 +11401,18 @@ var AZURE_STORAGE_CHECKS = {
|
|
|
11237
11401
|
};
|
|
11238
11402
|
var AZURE_SQL_CHECKS = {
|
|
11239
11403
|
list: "azure:sql:list",
|
|
11240
|
-
firewallRules: "azure:sql:firewallRules"
|
|
11404
|
+
firewallRules: "azure:sql:firewallRules",
|
|
11405
|
+
databases: "azure:sql:databases",
|
|
11406
|
+
transparentDataEncryption: "azure:sql:transparentDataEncryption"
|
|
11407
|
+
};
|
|
11408
|
+
var AZURE_KEYVAULT_CHECKS = {
|
|
11409
|
+
vaults: "azure:keyvault:vaults"
|
|
11410
|
+
};
|
|
11411
|
+
var AZURE_NETWORK_CHECKS = {
|
|
11412
|
+
virtualNetworks: "azure:network:virtualNetworks",
|
|
11413
|
+
networkWatchers: "azure:network:networkWatchers",
|
|
11414
|
+
flowLogs: "azure:network:flowLogs",
|
|
11415
|
+
networkInterfaces: "azure:network:networkInterfaces"
|
|
11241
11416
|
};
|
|
11242
11417
|
var AZURE_IAM_CHECKS = {
|
|
11243
11418
|
roleAssignments: "azure:iam:roleAssignments",
|
|
@@ -11296,6 +11471,44 @@ var sqlTlsDebil = {
|
|
|
11296
11471
|
requires: [AZURE_SQL_CHECKS.list],
|
|
11297
11472
|
evaluate: sqlTlsEvaluate
|
|
11298
11473
|
};
|
|
11474
|
+
function keyVaultSinPurgaEvaluate(observation) {
|
|
11475
|
+
if (!("purgeProtection" in observation.attributes)) {
|
|
11476
|
+
return { status: "unknown", check: AZURE_KEYVAULT_CHECKS.vaults, attribute: "purgeProtection" };
|
|
11477
|
+
}
|
|
11478
|
+
if (observation.attributes.purgeProtection === true) return { status: "ok" };
|
|
11479
|
+
return {
|
|
11480
|
+
status: "finding",
|
|
11481
|
+
evidence: { purgeProtection: false, softDelete: observation.attributes.softDelete ?? null }
|
|
11482
|
+
};
|
|
11483
|
+
}
|
|
11484
|
+
var keyVaultSinPurga = {
|
|
11485
|
+
id: "SEC_AZURE_ENCRYPTION_KEYVAULT_SIN_PURGA",
|
|
11486
|
+
scope: "resource",
|
|
11487
|
+
category: "security_encryption",
|
|
11488
|
+
severity: "medium",
|
|
11489
|
+
provider: "azure",
|
|
11490
|
+
kinds: ["key_vault"],
|
|
11491
|
+
requires: [AZURE_KEYVAULT_CHECKS.vaults],
|
|
11492
|
+
evaluate: keyVaultSinPurgaEvaluate
|
|
11493
|
+
};
|
|
11494
|
+
function sqlSinTdeEvaluate(observation) {
|
|
11495
|
+
const estado = observation.attributes.tdeState;
|
|
11496
|
+
if (typeof estado !== "string") {
|
|
11497
|
+
return { status: "unknown", check: AZURE_SQL_CHECKS.transparentDataEncryption, attribute: "tdeState" };
|
|
11498
|
+
}
|
|
11499
|
+
if (estado.toLowerCase() === "enabled") return { status: "ok" };
|
|
11500
|
+
return { status: "finding", evidence: { tdeState: estado } };
|
|
11501
|
+
}
|
|
11502
|
+
var sqlSinTde = {
|
|
11503
|
+
id: "SEC_AZURE_ENCRYPTION_SQL_SIN_TDE",
|
|
11504
|
+
scope: "resource",
|
|
11505
|
+
category: "security_encryption",
|
|
11506
|
+
severity: "high",
|
|
11507
|
+
provider: "azure",
|
|
11508
|
+
kinds: ["sql_database"],
|
|
11509
|
+
requires: [AZURE_SQL_CHECKS.list, AZURE_SQL_CHECKS.databases, AZURE_SQL_CHECKS.transparentDataEncryption],
|
|
11510
|
+
evaluate: sqlSinTdeEvaluate
|
|
11511
|
+
};
|
|
11299
11512
|
|
|
11300
11513
|
// ../../services/security/src/rules/azure/exposure.ts
|
|
11301
11514
|
function blobPublicoEvaluate(observation) {
|
|
@@ -11450,9 +11663,35 @@ var sinDiagnosticSettings = {
|
|
|
11450
11663
|
requires: [AZURE_LOGGING_CHECKS.diagnosticSettings],
|
|
11451
11664
|
evaluate: evaluate5
|
|
11452
11665
|
};
|
|
11666
|
+
function vnetSinFlowLogsEvaluate(observation) {
|
|
11667
|
+
const vnet = observation.attributes.vnetFlowLog;
|
|
11668
|
+
if (typeof vnet !== "boolean") {
|
|
11669
|
+
return { status: "unknown", check: AZURE_NETWORK_CHECKS.flowLogs, attribute: "vnetFlowLog" };
|
|
11670
|
+
}
|
|
11671
|
+
if (vnet) return { status: "ok" };
|
|
11672
|
+
const subnets = observation.attributes.subnets;
|
|
11673
|
+
if (!Array.isArray(subnets)) {
|
|
11674
|
+
return { status: "unknown", check: AZURE_NETWORK_CHECKS.networkInterfaces, attribute: "subnets" };
|
|
11675
|
+
}
|
|
11676
|
+
const sinRegistro = subnets.filter((subnet) => subnet.covered !== true && subnet.platform !== true).map((subnet) => subnet.name);
|
|
11677
|
+
if (sinRegistro.length === 0) return { status: "ok" };
|
|
11678
|
+
return { status: "finding", evidence: { unloggedSubnets: sinRegistro, subnetCount: subnets.length } };
|
|
11679
|
+
}
|
|
11680
|
+
var vnetSinFlowLogs = {
|
|
11681
|
+
id: "SEC_AZURE_LOGGING_VNET_SIN_FLOW_LOGS",
|
|
11682
|
+
scope: "resource",
|
|
11683
|
+
category: "security_logging",
|
|
11684
|
+
severity: "medium",
|
|
11685
|
+
provider: "azure",
|
|
11686
|
+
kinds: ["virtual_network"],
|
|
11687
|
+
// Las interfaces no van aquí: solo cuentan para subredes sin log propio, y su falta ya sale como `unknown` de esa VNet.
|
|
11688
|
+
requires: [AZURE_NETWORK_CHECKS.virtualNetworks, AZURE_NETWORK_CHECKS.networkWatchers, AZURE_NETWORK_CHECKS.flowLogs],
|
|
11689
|
+
evaluate: vnetSinFlowLogsEvaluate
|
|
11690
|
+
};
|
|
11453
11691
|
|
|
11454
11692
|
// ../../services/security/src/rules/azure/network.ts
|
|
11455
|
-
|
|
11693
|
+
var evaluate6 = (observation) => nsgAbierto(observation, adminPortsReached);
|
|
11694
|
+
function nsgAbierto(observation, reached) {
|
|
11456
11695
|
const ingress = observation.attributes.ingressRules;
|
|
11457
11696
|
if (!Array.isArray(ingress)) {
|
|
11458
11697
|
return { status: "unknown", check: AZURE_NSG_CHECKS.list, attribute: "ingressRules" };
|
|
@@ -11474,7 +11713,7 @@ function evaluate6(observation) {
|
|
|
11474
11713
|
}
|
|
11475
11714
|
if (!abierto) continue;
|
|
11476
11715
|
const protocol = typeof regla.protocol === "string" ? regla.protocol : "";
|
|
11477
|
-
const alcanzados =
|
|
11716
|
+
const alcanzados = reached(protocol, cadenas2(regla.ports));
|
|
11478
11717
|
if (alcanzados.length === 0) continue;
|
|
11479
11718
|
for (const puerto of alcanzados) puertos.add(puerto);
|
|
11480
11719
|
openings.push({ protocol: regla.protocol, ports: alcanzados, sources: regla.sources });
|
|
@@ -11497,17 +11736,31 @@ var nsgAdminAbierto = {
|
|
|
11497
11736
|
requires: [AZURE_NSG_CHECKS.list],
|
|
11498
11737
|
evaluate: evaluate6
|
|
11499
11738
|
};
|
|
11739
|
+
var nsgBdAbierto = {
|
|
11740
|
+
id: "SEC_AZURE_NETWORK_BD_ABIERTO",
|
|
11741
|
+
scope: "resource",
|
|
11742
|
+
category: "security_network",
|
|
11743
|
+
severity: "high",
|
|
11744
|
+
provider: "azure",
|
|
11745
|
+
kinds: ["nsg"],
|
|
11746
|
+
requires: [AZURE_NSG_CHECKS.list],
|
|
11747
|
+
evaluate: (observation) => nsgAbierto(observation, databasePortsReached)
|
|
11748
|
+
};
|
|
11500
11749
|
|
|
11501
11750
|
// ../../services/security/src/rules/azure/index.ts
|
|
11502
11751
|
var AZURE_SECURITY_RULES = [
|
|
11503
11752
|
nsgAdminAbierto,
|
|
11753
|
+
nsgBdAbierto,
|
|
11504
11754
|
storageBlobPublico,
|
|
11505
11755
|
sqlPublico,
|
|
11506
11756
|
storageTlsDebil,
|
|
11507
11757
|
sqlTlsDebil,
|
|
11758
|
+
keyVaultSinPurga,
|
|
11759
|
+
sqlSinTde,
|
|
11508
11760
|
rolPrivilegiado,
|
|
11509
11761
|
asignacionHuerfana,
|
|
11510
|
-
sinDiagnosticSettings
|
|
11762
|
+
sinDiagnosticSettings,
|
|
11763
|
+
vnetSinFlowLogs
|
|
11511
11764
|
];
|
|
11512
11765
|
|
|
11513
11766
|
// ../../services/security/src/rules/gcp/combos.ts
|
|
@@ -11579,7 +11832,16 @@ var GCP_COMPUTE_CHECKS = {
|
|
|
11579
11832
|
effectiveFirewalls: "gcp:compute:networks.getEffectiveFirewalls",
|
|
11580
11833
|
regions: "gcp:compute:regions",
|
|
11581
11834
|
regionFirewallPolicies: "gcp:compute:regionNetworkFirewallPolicies",
|
|
11582
|
-
sharedVpcHost: "gcp:compute:sharedVpcHostFirewalls"
|
|
11835
|
+
sharedVpcHost: "gcp:compute:sharedVpcHostFirewalls",
|
|
11836
|
+
subnetworks: "gcp:compute:subnetworks"
|
|
11837
|
+
};
|
|
11838
|
+
var GCP_NETWORK_MANAGEMENT_CHECKS = {
|
|
11839
|
+
vpcFlowLogsConfigs: "gcp:networkmanagement:vpcFlowLogsConfigs"
|
|
11840
|
+
};
|
|
11841
|
+
var GCP_KMS_CHECKS = {
|
|
11842
|
+
locations: "gcp:kms:locations",
|
|
11843
|
+
keyRings: "gcp:kms:keyRings",
|
|
11844
|
+
cryptoKeys: "gcp:kms:cryptoKeys"
|
|
11583
11845
|
};
|
|
11584
11846
|
var GCP_SQL_CHECKS = {
|
|
11585
11847
|
instances: "gcp:sql:instances"
|
|
@@ -11649,6 +11911,63 @@ var sqlSinCmek = {
|
|
|
11649
11911
|
requires: [GCP_SQL_CHECKS.instances],
|
|
11650
11912
|
evaluate: sqlSinCmekEvaluate
|
|
11651
11913
|
};
|
|
11914
|
+
var SOLO_CIFRADO = /* @__PURE__ */ new Set(["ENCRYPTED_ONLY", "TRUSTED_CLIENT_CERTIFICATE_REQUIRED"]);
|
|
11915
|
+
function sqlSinSslEvaluate(observation) {
|
|
11916
|
+
const modo = observation.attributes.sslMode;
|
|
11917
|
+
if (typeof modo === "string" && modo.length > 0) {
|
|
11918
|
+
if (SOLO_CIFRADO.has(modo)) return { status: "ok" };
|
|
11919
|
+
return { status: "finding", evidence: { sslMode: modo, ipv4Enabled: observation.attributes.ipv4Enabled ?? null } };
|
|
11920
|
+
}
|
|
11921
|
+
const requiere = observation.attributes.requireSsl;
|
|
11922
|
+
if (requiere === true) return { status: "ok" };
|
|
11923
|
+
if (!("sslMode" in observation.attributes)) {
|
|
11924
|
+
return { status: "unknown", check: GCP_SQL_CHECKS.instances, attribute: "sslMode" };
|
|
11925
|
+
}
|
|
11926
|
+
if (requiere !== false && requiere !== null) {
|
|
11927
|
+
return { status: "unknown", check: GCP_SQL_CHECKS.instances, attribute: "requireSsl" };
|
|
11928
|
+
}
|
|
11929
|
+
return { status: "finding", evidence: { sslMode: null, requireSsl: requiere, ipv4Enabled: observation.attributes.ipv4Enabled ?? null } };
|
|
11930
|
+
}
|
|
11931
|
+
var sqlSinSsl = {
|
|
11932
|
+
id: "SEC_GCP_ENCRYPTION_SQL_SIN_SSL",
|
|
11933
|
+
scope: "resource",
|
|
11934
|
+
category: "security_encryption",
|
|
11935
|
+
severity: "medium",
|
|
11936
|
+
provider: "gcp",
|
|
11937
|
+
kinds: ["cloud_sql_instance"],
|
|
11938
|
+
requires: [GCP_SQL_CHECKS.instances],
|
|
11939
|
+
evaluate: sqlSinSslEvaluate
|
|
11940
|
+
};
|
|
11941
|
+
var KMS_MAX_ROTATION_DAYS = 90;
|
|
11942
|
+
function segundos(periodo) {
|
|
11943
|
+
const m = /^(\d+(?:\.\d+)?)s$/.exec(periodo);
|
|
11944
|
+
return m ? Number(m[1]) : void 0;
|
|
11945
|
+
}
|
|
11946
|
+
function kmsSinRotacionEvaluate2(observation) {
|
|
11947
|
+
const a = observation.attributes;
|
|
11948
|
+
if (!("purpose" in a) || !("primaryState" in a) || !("rotationPeriod" in a)) {
|
|
11949
|
+
return { status: "unknown", check: GCP_KMS_CHECKS.cryptoKeys, attribute: "rotationPeriod" };
|
|
11950
|
+
}
|
|
11951
|
+
if (a.purpose !== "ENCRYPT_DECRYPT" || a.primaryState !== "ENABLED") return { status: "ok" };
|
|
11952
|
+
if (a.importOnly === true || typeof a.protectionLevel === "string" && a.protectionLevel.startsWith("EXTERNAL")) {
|
|
11953
|
+
return { status: "ok" };
|
|
11954
|
+
}
|
|
11955
|
+
if (a.rotationPeriod === null) return { status: "finding", evidence: { rotationPeriod: null } };
|
|
11956
|
+
const s = typeof a.rotationPeriod === "string" ? segundos(a.rotationPeriod) : void 0;
|
|
11957
|
+
if (s === void 0) return { status: "unknown", check: GCP_KMS_CHECKS.cryptoKeys, attribute: "rotationPeriod" };
|
|
11958
|
+
if (s <= KMS_MAX_ROTATION_DAYS * 86400) return { status: "ok" };
|
|
11959
|
+
return { status: "finding", evidence: { rotationPeriodDays: Math.round(s / 86400) } };
|
|
11960
|
+
}
|
|
11961
|
+
var kmsSinRotacion2 = {
|
|
11962
|
+
id: "SEC_GCP_ENCRYPTION_KMS_SIN_ROTACION",
|
|
11963
|
+
scope: "resource",
|
|
11964
|
+
category: "security_encryption",
|
|
11965
|
+
severity: "medium",
|
|
11966
|
+
provider: "gcp",
|
|
11967
|
+
kinds: ["kms_key"],
|
|
11968
|
+
requires: [GCP_KMS_CHECKS.locations, GCP_KMS_CHECKS.keyRings, GCP_KMS_CHECKS.cryptoKeys],
|
|
11969
|
+
evaluate: kmsSinRotacionEvaluate2
|
|
11970
|
+
};
|
|
11652
11971
|
|
|
11653
11972
|
// ../../services/security/src/rules/gcp/exposure.ts
|
|
11654
11973
|
function evaluate7(observation) {
|
|
@@ -11884,9 +12203,42 @@ var sinExportSink = {
|
|
|
11884
12203
|
requires: [GCP_LOGGING_CHECKS.sinks],
|
|
11885
12204
|
evaluate: evaluate8
|
|
11886
12205
|
};
|
|
12206
|
+
function vpcSinFlowLogsEvaluate(observation) {
|
|
12207
|
+
const subnets = observation.attributes.subnets;
|
|
12208
|
+
if (!Array.isArray(subnets)) {
|
|
12209
|
+
return { status: "unknown", check: GCP_COMPUTE_CHECKS.subnetworks, attribute: "subnets" };
|
|
12210
|
+
}
|
|
12211
|
+
const lista = subnets;
|
|
12212
|
+
const sinRegistro = lista.filter((subnet) => subnet.covered === false);
|
|
12213
|
+
if (sinRegistro.length > 0) {
|
|
12214
|
+
return {
|
|
12215
|
+
status: "finding",
|
|
12216
|
+
evidence: { unloggedSubnets: sinRegistro.map((subnet) => `${String(subnet.region)}/${String(subnet.name)}`), subnetCount: lista.length }
|
|
12217
|
+
};
|
|
12218
|
+
}
|
|
12219
|
+
if (lista.some((subnet) => subnet.covered !== true)) {
|
|
12220
|
+
return { status: "unknown", check: GCP_NETWORK_MANAGEMENT_CHECKS.vpcFlowLogsConfigs, attribute: "subnets" };
|
|
12221
|
+
}
|
|
12222
|
+
if (observation.attributes.subnetsComplete !== true) {
|
|
12223
|
+
return { status: "unknown", check: GCP_COMPUTE_CHECKS.subnetworks, attribute: "subnets" };
|
|
12224
|
+
}
|
|
12225
|
+
return { status: "ok" };
|
|
12226
|
+
}
|
|
12227
|
+
var vpcSinFlowLogs = {
|
|
12228
|
+
id: "SEC_GCP_LOGGING_VPC_SIN_FLOW_LOGS",
|
|
12229
|
+
scope: "resource",
|
|
12230
|
+
category: "security_logging",
|
|
12231
|
+
severity: "medium",
|
|
12232
|
+
provider: "gcp",
|
|
12233
|
+
kinds: ["vpc_network"],
|
|
12234
|
+
// Las configs de Network Management no van aquí: una red con `logConfig` en cada subred ya está bien sin ellas.
|
|
12235
|
+
requires: [GCP_COMPUTE_CHECKS.subnetworks],
|
|
12236
|
+
evaluate: vpcSinFlowLogsEvaluate
|
|
12237
|
+
};
|
|
11887
12238
|
|
|
11888
12239
|
// ../../services/security/src/rules/gcp/network.ts
|
|
11889
|
-
|
|
12240
|
+
var evaluate9 = (observation) => firewallAbierto(observation, adminPortsReached);
|
|
12241
|
+
function firewallAbierto(observation, reached) {
|
|
11890
12242
|
const direction = observation.attributes.direction;
|
|
11891
12243
|
const disabled = observation.attributes.disabled;
|
|
11892
12244
|
const sources = observation.attributes.sourceRanges;
|
|
@@ -11905,7 +12257,7 @@ function evaluate9(observation) {
|
|
|
11905
12257
|
if (typeof entry !== "object" || entry === null) continue;
|
|
11906
12258
|
const rule = entry;
|
|
11907
12259
|
const protocol = typeof rule.protocol === "string" ? rule.protocol : "";
|
|
11908
|
-
for (const puerto of
|
|
12260
|
+
for (const puerto of reached(protocol, cadenas2(rule.ports))) puertos.add(puerto);
|
|
11909
12261
|
}
|
|
11910
12262
|
if (puertos.size === 0) return { status: "ok" };
|
|
11911
12263
|
return {
|
|
@@ -12065,6 +12417,16 @@ var firewallAdminAbierto = {
|
|
|
12065
12417
|
requires: [GCP_COMPUTE_CHECKS.firewalls],
|
|
12066
12418
|
evaluate: evaluate9
|
|
12067
12419
|
};
|
|
12420
|
+
var firewallBdAbierto = {
|
|
12421
|
+
id: "SEC_GCP_NETWORK_BD_ABIERTO",
|
|
12422
|
+
scope: "resource",
|
|
12423
|
+
category: "security_network",
|
|
12424
|
+
severity: "high",
|
|
12425
|
+
provider: "gcp",
|
|
12426
|
+
kinds: ["firewall"],
|
|
12427
|
+
requires: [GCP_COMPUTE_CHECKS.firewalls],
|
|
12428
|
+
evaluate: (observation) => firewallAbierto(observation, databasePortsReached)
|
|
12429
|
+
};
|
|
12068
12430
|
|
|
12069
12431
|
// ../../services/security/src/rules/gcp/secrets.ts
|
|
12070
12432
|
var SOSPECHOSAS2 = ["PASSWORD", "PASSWD", "SECRET", "TOKEN", "APIKEY", "API_KEY", "PRIVATE_KEY", "CREDENTIAL"];
|
|
@@ -12099,11 +12461,14 @@ var secretoEnFunction = {
|
|
|
12099
12461
|
// ../../services/security/src/rules/gcp/index.ts
|
|
12100
12462
|
var GCP_SECURITY_RULES = [
|
|
12101
12463
|
firewallAdminAbierto,
|
|
12464
|
+
firewallBdAbierto,
|
|
12102
12465
|
politicaAdminAbierta,
|
|
12103
12466
|
sqlPublico2,
|
|
12104
12467
|
gcsPublico,
|
|
12105
12468
|
discoSinCmek,
|
|
12106
12469
|
sqlSinCmek,
|
|
12470
|
+
sqlSinSsl,
|
|
12471
|
+
kmsSinRotacion2,
|
|
12107
12472
|
secretoEnFunction,
|
|
12108
12473
|
rolPrimitivo,
|
|
12109
12474
|
claveSaUserManaged,
|
|
@@ -12111,7 +12476,8 @@ var GCP_SECURITY_RULES = [
|
|
|
12111
12476
|
claveSaSinUso,
|
|
12112
12477
|
permisosSinUso2,
|
|
12113
12478
|
auditIncompleto,
|
|
12114
|
-
sinExportSink
|
|
12479
|
+
sinExportSink,
|
|
12480
|
+
vpcSinFlowLogs
|
|
12115
12481
|
];
|
|
12116
12482
|
|
|
12117
12483
|
// src/security/evaluate.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aaa-sentinel/iac-cost",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Estimate the monthly cost delta of a Terraform plan, check it against the AAA Sentinel security rules, open security fixes as pull requests and report how the security posture changed, from your CI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|