blockintel-gate-sdk 0.3.2 → 0.3.4
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 +56 -6
- 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/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# BlockIntel Gate SDK
|
|
2
2
|
|
|
3
|
-
Production-grade TypeScript/Node.js SDK for [BlockIntel Gate](https://
|
|
3
|
+
Production-grade TypeScript/Node.js SDK for [BlockIntel Gate](https://blockintelai.com) Hot Path API.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -102,6 +102,47 @@ const response = await gate.evaluate({
|
|
|
102
102
|
});
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
+
### Local Development with Gate Local
|
|
106
|
+
|
|
107
|
+
For local development and testing, use **Gate Local** - a Docker container that emulates the Gate Hot Path:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Start Gate Local
|
|
111
|
+
docker pull blockintelai/gate-local:latest
|
|
112
|
+
docker run -d --name gate-local -p 3000:3000 blockintelai/gate-local:latest
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Then configure your client for local mode:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { GateClient } from '@blockintel/gate-sdk';
|
|
119
|
+
|
|
120
|
+
// Local development configuration
|
|
121
|
+
const gate = new GateClient({
|
|
122
|
+
baseUrl: 'http://localhost:3000', // Gate Local endpoint
|
|
123
|
+
tenantId: 'local-dev', // Any tenant ID (ignored in local mode)
|
|
124
|
+
local: true, // Enable local mode (disables auth/heartbeat)
|
|
125
|
+
auth: {
|
|
126
|
+
mode: 'apiKey',
|
|
127
|
+
apiKey: 'local-dev-key' // Any API key (ignored in local mode)
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Evaluate transactions locally
|
|
132
|
+
const response = await gate.evaluate({
|
|
133
|
+
txIntent: {
|
|
134
|
+
toAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
|
|
135
|
+
value: '1000000000000000000', // 1 ETH in wei
|
|
136
|
+
valueUsd: 2500.0,
|
|
137
|
+
chainId: 1,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
console.log(`Decision: ${response.decision}`);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**📚 Full Local Development Guide**: See [Gate Local Quick Start Guide](https://docs.blockintelai.com/gate/local-development) for complete setup instructions, trading bot integration examples, and troubleshooting.
|
|
145
|
+
|
|
105
146
|
### Step-Up Polling
|
|
106
147
|
|
|
107
148
|
```typescript
|
|
@@ -159,7 +200,7 @@ When step-up is disabled, the SDK treats `REQUIRE_STEP_UP` as `BLOCK` by default
|
|
|
159
200
|
|
|
160
201
|
```bash
|
|
161
202
|
# Required
|
|
162
|
-
GATE_BASE_URL=https://gate.blockintelai.
|
|
203
|
+
GATE_BASE_URL=https://gate.blockintelai.com
|
|
163
204
|
GATE_TENANT_ID=your-tenant-id
|
|
164
205
|
|
|
165
206
|
# HMAC Authentication
|
|
@@ -353,12 +394,12 @@ The heartbeat manager is automatically configured based on your `GateClientConfi
|
|
|
353
394
|
|
|
354
395
|
```typescript
|
|
355
396
|
const gate = new GateClient({
|
|
356
|
-
baseUrl: 'https://gate.blockintelai.
|
|
397
|
+
baseUrl: 'https://gate.blockintelai.com', // Hot Path URL
|
|
357
398
|
tenantId: 'your-tenant-id',
|
|
358
399
|
auth: { mode: 'hmac', ... },
|
|
359
400
|
// Heartbeat manager uses baseUrl to infer Control Plane URL
|
|
360
401
|
// Or explicitly set controlPlaneUrl if different
|
|
361
|
-
controlPlaneUrl: 'https://control-plane.blockintelai.
|
|
402
|
+
controlPlaneUrl: 'https://control-plane.blockintelai.com', // Optional
|
|
362
403
|
signerId: 'my-signer-id', // Optional: signerId for heartbeat (if known upfront)
|
|
363
404
|
heartbeatRefreshIntervalSeconds: 10, // Optional: heartbeat refresh interval (default: 10s)
|
|
364
405
|
});
|
|
@@ -455,13 +496,22 @@ See [examples](./examples) directory for complete examples:
|
|
|
455
496
|
|
|
456
497
|
See [PUBLISHING.md](./PUBLISHING.md) for detailed publishing instructions.
|
|
457
498
|
|
|
499
|
+
**Quick steps:**
|
|
500
|
+
1. Update version in `package.json`
|
|
501
|
+
2. Create GitHub release tag
|
|
502
|
+
3. GitHub Actions publishes to NPM automatically
|
|
503
|
+
|
|
458
504
|
## License
|
|
459
505
|
|
|
460
506
|
MIT License - see [LICENSE](./LICENSE) file.
|
|
461
507
|
|
|
462
508
|
## Support
|
|
463
509
|
|
|
464
|
-
- **Documentation:** https://docs.
|
|
510
|
+
- **Documentation:** https://docs.blockintelai.com
|
|
465
511
|
- **Issues:** https://github.com/4KInc/blockintel-ai/issues
|
|
466
|
-
- **Email:** support@
|
|
512
|
+
- **Email:** support@blockintelai.com
|
|
513
|
+
|
|
514
|
+
## Keywords
|
|
515
|
+
|
|
516
|
+
blockintel, gate, sdk, defense, crypto, security, transaction, evaluation
|
|
467
517
|
|
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.blockintelai.com/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.blockintelai.com/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.blockintelai.com/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.blockintelai.com/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
|