blockintel-gate-sdk 0.3.2 → 0.3.3
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/index.cjs +277 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +277 -0
- package/dist/index.js.map +1 -1
- package/package.json +15 -3
package/dist/index.cjs
CHANGED
|
@@ -1250,6 +1250,250 @@ var HeartbeatManager = class {
|
|
|
1250
1250
|
}
|
|
1251
1251
|
};
|
|
1252
1252
|
|
|
1253
|
+
// src/security/IamPermissionRiskChecker.ts
|
|
1254
|
+
var IamPermissionRiskChecker = class {
|
|
1255
|
+
options;
|
|
1256
|
+
constructor(options) {
|
|
1257
|
+
this.options = options;
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Perform synchronous IAM permission risk check
|
|
1261
|
+
*
|
|
1262
|
+
* Performs quick checks (credentials, environment markers) synchronously.
|
|
1263
|
+
* In HARD mode, throws error if risk detected and override not set.
|
|
1264
|
+
*
|
|
1265
|
+
* Use this for blocking initialization checks.
|
|
1266
|
+
*/
|
|
1267
|
+
checkSync() {
|
|
1268
|
+
const checks = [];
|
|
1269
|
+
const credentialsCheck = this.checkAwsCredentials();
|
|
1270
|
+
if (credentialsCheck.hasRisk) {
|
|
1271
|
+
checks.push(credentialsCheck);
|
|
1272
|
+
}
|
|
1273
|
+
const envCheck = this.checkEnvironmentMarkers();
|
|
1274
|
+
if (envCheck.hasRisk) {
|
|
1275
|
+
checks.push(envCheck);
|
|
1276
|
+
}
|
|
1277
|
+
const highestConfidence = this.getHighestConfidence(checks);
|
|
1278
|
+
const highestRisk = checks.find((c) => c.confidence === highestConfidence);
|
|
1279
|
+
if (!highestRisk || !highestRisk.hasRisk) {
|
|
1280
|
+
return {
|
|
1281
|
+
hasRisk: false,
|
|
1282
|
+
confidence: "LOW",
|
|
1283
|
+
details: "No IAM permission risk detected (synchronous check)"
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
if (this.options.enforcementMode === "HARD" && !this.options.allowInsecureKmsSignPermission) {
|
|
1287
|
+
const errorMessage = this.buildErrorMessage(highestRisk);
|
|
1288
|
+
throw new Error(errorMessage);
|
|
1289
|
+
}
|
|
1290
|
+
this.logWarning(highestRisk);
|
|
1291
|
+
return highestRisk;
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Perform full IAM permission risk check (including async IAM simulation)
|
|
1295
|
+
*
|
|
1296
|
+
* Returns risk assessment with confidence level.
|
|
1297
|
+
* In HARD mode, throws error if risk detected and override not set.
|
|
1298
|
+
*/
|
|
1299
|
+
async check() {
|
|
1300
|
+
const syncResult = this.checkSync();
|
|
1301
|
+
const simulationCheck = await this.checkIamSimulation();
|
|
1302
|
+
if (simulationCheck.hasRisk) {
|
|
1303
|
+
if (this.options.enforcementMode === "HARD" && !this.options.allowInsecureKmsSignPermission) {
|
|
1304
|
+
const errorMessage = this.buildErrorMessage(simulationCheck);
|
|
1305
|
+
throw new Error(errorMessage);
|
|
1306
|
+
}
|
|
1307
|
+
this.logWarning(simulationCheck);
|
|
1308
|
+
return simulationCheck;
|
|
1309
|
+
}
|
|
1310
|
+
return syncResult;
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Check if AWS credentials are present
|
|
1314
|
+
*/
|
|
1315
|
+
checkAwsCredentials() {
|
|
1316
|
+
const hasEnvVars = !!(process.env.AWS_ACCESS_KEY_ID || process.env.AWS_SECRET_ACCESS_KEY || process.env.AWS_SESSION_TOKEN);
|
|
1317
|
+
const hasRoleCredentials = !!(process.env.AWS_ROLE_ARN || process.env.AWS_WEB_IDENTITY_TOKEN_FILE || process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI);
|
|
1318
|
+
if (hasEnvVars || hasRoleCredentials) {
|
|
1319
|
+
return {
|
|
1320
|
+
hasRisk: true,
|
|
1321
|
+
riskType: "AWS_CREDENTIALS_DETECTED",
|
|
1322
|
+
confidence: "MEDIUM",
|
|
1323
|
+
details: "AWS credentials detected in environment. Application may have direct KMS signing permissions.",
|
|
1324
|
+
remediation: "Remove kms:Sign permission from application role. See https://docs.blockintel.ai/gate/IAM_HARDENING"
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
return {
|
|
1328
|
+
hasRisk: false,
|
|
1329
|
+
confidence: "LOW",
|
|
1330
|
+
details: "No AWS credentials detected in environment variables"
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Check IAM permissions using simulation API (if available)
|
|
1335
|
+
*/
|
|
1336
|
+
async checkIamSimulation() {
|
|
1337
|
+
try {
|
|
1338
|
+
const iamModule = await import('@aws-sdk/client-iam').catch(() => null);
|
|
1339
|
+
if (!iamModule || !iamModule.IAMClient || !iamModule.SimulatePrincipalPolicyCommand) {
|
|
1340
|
+
return {
|
|
1341
|
+
hasRisk: false,
|
|
1342
|
+
confidence: "LOW",
|
|
1343
|
+
details: "AWS SDK not available for IAM simulation"
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
const { IAMClient, SimulatePrincipalPolicyCommand } = iamModule;
|
|
1347
|
+
const principalArn = await this.getCurrentPrincipalArn();
|
|
1348
|
+
if (!principalArn) {
|
|
1349
|
+
return {
|
|
1350
|
+
hasRisk: false,
|
|
1351
|
+
confidence: "LOW",
|
|
1352
|
+
details: "Could not determine current principal ARN for simulation"
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
const client = new IAMClient({});
|
|
1356
|
+
const command = new SimulatePrincipalPolicyCommand({
|
|
1357
|
+
PolicySourceArn: principalArn,
|
|
1358
|
+
ActionNames: ["kms:Sign"],
|
|
1359
|
+
ResourceArns: this.options.kmsKeyIds?.map((id) => `arn:aws:kms:*:*:key/${id}`) || ["arn:aws:kms:*:*:key/*"]
|
|
1360
|
+
});
|
|
1361
|
+
const response = await client.send(command).catch(() => null);
|
|
1362
|
+
if (!response) {
|
|
1363
|
+
return {
|
|
1364
|
+
hasRisk: false,
|
|
1365
|
+
confidence: "LOW",
|
|
1366
|
+
details: "IAM simulation not available (may require additional permissions)"
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
const allowsSign = response.EvaluationResults?.some(
|
|
1370
|
+
(result) => result.EvalDecision === "allowed" || result.EvalDecision === "explicitAllow"
|
|
1371
|
+
);
|
|
1372
|
+
if (allowsSign) {
|
|
1373
|
+
return {
|
|
1374
|
+
hasRisk: true,
|
|
1375
|
+
riskType: "DIRECT_KMS_SIGN_PERMISSION",
|
|
1376
|
+
confidence: "HIGH",
|
|
1377
|
+
details: `IAM simulation confirms principal ${principalArn} has kms:Sign permission. Direct KMS signing can bypass Gate.`,
|
|
1378
|
+
remediation: "Remove kms:Sign permission from application role. See https://docs.blockintel.ai/gate/IAM_HARDENING"
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
return {
|
|
1382
|
+
hasRisk: false,
|
|
1383
|
+
confidence: "HIGH",
|
|
1384
|
+
details: "IAM simulation confirms no kms:Sign permission"
|
|
1385
|
+
};
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
return {
|
|
1388
|
+
hasRisk: false,
|
|
1389
|
+
confidence: "LOW",
|
|
1390
|
+
details: `IAM simulation failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Check environment markers that suggest direct KMS usage
|
|
1396
|
+
*/
|
|
1397
|
+
checkEnvironmentMarkers() {
|
|
1398
|
+
const markers = [
|
|
1399
|
+
"KMS_KEY_ID",
|
|
1400
|
+
"AWS_KMS_KEY_ID",
|
|
1401
|
+
"KMS_KEY_ARN",
|
|
1402
|
+
"AWS_KMS_KEY_ARN"
|
|
1403
|
+
];
|
|
1404
|
+
const foundMarkers = markers.filter((marker) => process.env[marker]);
|
|
1405
|
+
if (foundMarkers.length > 0) {
|
|
1406
|
+
return {
|
|
1407
|
+
hasRisk: true,
|
|
1408
|
+
riskType: "ENVIRONMENT_MARKERS",
|
|
1409
|
+
confidence: "LOW",
|
|
1410
|
+
details: `Environment markers suggest direct KMS usage: ${foundMarkers.join(", ")}`,
|
|
1411
|
+
remediation: "Review environment variables and ensure KMS access is gated through Gate SDK"
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
return {
|
|
1415
|
+
hasRisk: false,
|
|
1416
|
+
confidence: "LOW",
|
|
1417
|
+
details: "No environment markers suggesting direct KMS usage"
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Get current principal ARN (best-effort)
|
|
1422
|
+
*/
|
|
1423
|
+
async getCurrentPrincipalArn() {
|
|
1424
|
+
try {
|
|
1425
|
+
const stsModule = await import('@aws-sdk/client-sts').catch(() => null);
|
|
1426
|
+
if (!stsModule || !stsModule.STSClient || !stsModule.GetCallerIdentityCommand) {
|
|
1427
|
+
return null;
|
|
1428
|
+
}
|
|
1429
|
+
const { STSClient, GetCallerIdentityCommand } = stsModule;
|
|
1430
|
+
const client = new STSClient({});
|
|
1431
|
+
const command = new GetCallerIdentityCommand({});
|
|
1432
|
+
const response = await client.send(command).catch(() => null);
|
|
1433
|
+
if (response?.Arn) {
|
|
1434
|
+
return response.Arn;
|
|
1435
|
+
}
|
|
1436
|
+
} catch (error) {
|
|
1437
|
+
}
|
|
1438
|
+
return null;
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Get highest confidence level from checks
|
|
1442
|
+
*/
|
|
1443
|
+
getHighestConfidence(checks) {
|
|
1444
|
+
if (checks.some((c) => c.confidence === "HIGH")) {
|
|
1445
|
+
return "HIGH";
|
|
1446
|
+
}
|
|
1447
|
+
if (checks.some((c) => c.confidence === "MEDIUM")) {
|
|
1448
|
+
return "MEDIUM";
|
|
1449
|
+
}
|
|
1450
|
+
return "LOW";
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Build error message for HARD mode
|
|
1454
|
+
*/
|
|
1455
|
+
buildErrorMessage(result) {
|
|
1456
|
+
const parts = [
|
|
1457
|
+
"[GATE ERROR] Hard enforcement mode blocked initialization:",
|
|
1458
|
+
` - IAM permission risk: ${result.details}`,
|
|
1459
|
+
` - Risk type: ${result.riskType}`,
|
|
1460
|
+
` - Confidence: ${result.confidence}`,
|
|
1461
|
+
` - Tenant ID: ${this.options.tenantId}`
|
|
1462
|
+
];
|
|
1463
|
+
if (this.options.signerId) {
|
|
1464
|
+
parts.push(` - Signer ID: ${this.options.signerId}`);
|
|
1465
|
+
}
|
|
1466
|
+
if (this.options.environment) {
|
|
1467
|
+
parts.push(` - Environment: ${this.options.environment}`);
|
|
1468
|
+
}
|
|
1469
|
+
if (result.remediation) {
|
|
1470
|
+
parts.push(` - Remediation: ${result.remediation}`);
|
|
1471
|
+
}
|
|
1472
|
+
parts.push(" - See: https://docs.blockintel.ai/gate/IAM_HARDENING");
|
|
1473
|
+
parts.push(` - Override: Set allowInsecureKmsSignPermission=true (not recommended for production)`);
|
|
1474
|
+
return parts.join("\n");
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Log warning (SOFT mode or override set)
|
|
1478
|
+
*/
|
|
1479
|
+
logWarning(result) {
|
|
1480
|
+
const logData = {
|
|
1481
|
+
level: "WARN",
|
|
1482
|
+
message: "IAM permission risk detected",
|
|
1483
|
+
tenantId: this.options.tenantId,
|
|
1484
|
+
signerId: this.options.signerId,
|
|
1485
|
+
environment: this.options.environment,
|
|
1486
|
+
enforcementMode: this.options.enforcementMode,
|
|
1487
|
+
riskType: result.riskType,
|
|
1488
|
+
confidence: result.confidence,
|
|
1489
|
+
details: result.details,
|
|
1490
|
+
remediation: result.remediation,
|
|
1491
|
+
documentation: "https://docs.blockintel.ai/gate/IAM_HARDENING"
|
|
1492
|
+
};
|
|
1493
|
+
console.warn("[GATE WARNING]", JSON.stringify(logData, null, 2));
|
|
1494
|
+
}
|
|
1495
|
+
};
|
|
1496
|
+
|
|
1253
1497
|
// src/client/GateClient.ts
|
|
1254
1498
|
var GateClient = class {
|
|
1255
1499
|
config;
|
|
@@ -1328,6 +1572,39 @@ var GateClient = class {
|
|
|
1328
1572
|
});
|
|
1329
1573
|
this.heartbeatManager.start();
|
|
1330
1574
|
}
|
|
1575
|
+
if (!config.local) {
|
|
1576
|
+
const enforcementMode = config.enforcementMode || "SOFT";
|
|
1577
|
+
const allowInsecureKmsSignPermission = config.allowInsecureKmsSignPermission ?? enforcementMode === "SOFT";
|
|
1578
|
+
const riskChecker = new IamPermissionRiskChecker({
|
|
1579
|
+
tenantId: config.tenantId,
|
|
1580
|
+
signerId: config.signerId,
|
|
1581
|
+
environment: config.environment,
|
|
1582
|
+
enforcementMode,
|
|
1583
|
+
allowInsecureKmsSignPermission,
|
|
1584
|
+
kmsKeyIds: config.kmsKeyIds
|
|
1585
|
+
});
|
|
1586
|
+
riskChecker.checkSync();
|
|
1587
|
+
this.performIamRiskCheckAsync(riskChecker, enforcementMode).catch((error) => {
|
|
1588
|
+
if (enforcementMode === "SOFT" || allowInsecureKmsSignPermission) {
|
|
1589
|
+
console.warn("[GATE CLIENT] Async IAM risk check warning:", error instanceof Error ? error.message : String(error));
|
|
1590
|
+
} else {
|
|
1591
|
+
console.error("[GATE CLIENT] Async IAM risk check found risk after initialization:", error);
|
|
1592
|
+
}
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
/**
|
|
1597
|
+
* Perform async IAM permission risk check (non-blocking)
|
|
1598
|
+
*
|
|
1599
|
+
* Performs async IAM simulation check in background.
|
|
1600
|
+
* Logs warnings but doesn't block (initialization already completed).
|
|
1601
|
+
*/
|
|
1602
|
+
async performIamRiskCheckAsync(riskChecker, enforcementMode) {
|
|
1603
|
+
try {
|
|
1604
|
+
await riskChecker.check();
|
|
1605
|
+
} catch (error) {
|
|
1606
|
+
console.warn("[GATE CLIENT] Async IAM risk check warning:", error instanceof Error ? error.message : String(error));
|
|
1607
|
+
}
|
|
1331
1608
|
}
|
|
1332
1609
|
/**
|
|
1333
1610
|
* Evaluate a transaction defense request
|