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