blockintel-gate-sdk 0.4.4 → 0.4.6
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 +120 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +120 -3
- package/dist/index.js.map +1 -1
- package/dist/pilot/index.cjs +120 -3
- package/dist/pilot/index.cjs.map +1 -1
- package/dist/pilot/index.js +120 -3
- package/dist/pilot/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -273,6 +273,11 @@ declare class GateClient {
|
|
|
273
273
|
* Logs warnings but doesn't block (initialization already completed).
|
|
274
274
|
*/
|
|
275
275
|
private performIamRiskCheckAsync;
|
|
276
|
+
/**
|
|
277
|
+
* Warn if the local SDK mode is SHADOW but the server's adoption stage is enforcing.
|
|
278
|
+
* Runs non-blocking after heartbeat startup; never throws.
|
|
279
|
+
*/
|
|
280
|
+
private checkAdoptionStageMismatch;
|
|
276
281
|
/**
|
|
277
282
|
* Evaluate a transaction defense request
|
|
278
283
|
*
|
|
@@ -597,6 +602,8 @@ declare class HeartbeatManager {
|
|
|
597
602
|
private evictionTimer;
|
|
598
603
|
private started;
|
|
599
604
|
private maxBackoffSeconds;
|
|
605
|
+
/** Server's current adoption stage for this tenant (cached from heartbeat response) */
|
|
606
|
+
private adoptionStage;
|
|
600
607
|
private readonly maxSigners;
|
|
601
608
|
private readonly signerIdleTtlMs;
|
|
602
609
|
private readonly localRateLimitMs;
|
|
@@ -662,6 +669,12 @@ declare class HeartbeatManager {
|
|
|
662
669
|
* Get client instance ID (for tracking)
|
|
663
670
|
*/
|
|
664
671
|
getClientInstanceId(): string;
|
|
672
|
+
/**
|
|
673
|
+
* Get the server's current adoption stage for this tenant.
|
|
674
|
+
* Populated after the first successful heartbeat response.
|
|
675
|
+
* Returns null if not yet received.
|
|
676
|
+
*/
|
|
677
|
+
getAdoptionStage(): string | null;
|
|
665
678
|
}
|
|
666
679
|
|
|
667
680
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -273,6 +273,11 @@ declare class GateClient {
|
|
|
273
273
|
* Logs warnings but doesn't block (initialization already completed).
|
|
274
274
|
*/
|
|
275
275
|
private performIamRiskCheckAsync;
|
|
276
|
+
/**
|
|
277
|
+
* Warn if the local SDK mode is SHADOW but the server's adoption stage is enforcing.
|
|
278
|
+
* Runs non-blocking after heartbeat startup; never throws.
|
|
279
|
+
*/
|
|
280
|
+
private checkAdoptionStageMismatch;
|
|
276
281
|
/**
|
|
277
282
|
* Evaluate a transaction defense request
|
|
278
283
|
*
|
|
@@ -597,6 +602,8 @@ declare class HeartbeatManager {
|
|
|
597
602
|
private evictionTimer;
|
|
598
603
|
private started;
|
|
599
604
|
private maxBackoffSeconds;
|
|
605
|
+
/** Server's current adoption stage for this tenant (cached from heartbeat response) */
|
|
606
|
+
private adoptionStage;
|
|
600
607
|
private readonly maxSigners;
|
|
601
608
|
private readonly signerIdleTtlMs;
|
|
602
609
|
private readonly localRateLimitMs;
|
|
@@ -662,6 +669,12 @@ declare class HeartbeatManager {
|
|
|
662
669
|
* Get client instance ID (for tracking)
|
|
663
670
|
*/
|
|
664
671
|
getClientInstanceId(): string;
|
|
672
|
+
/**
|
|
673
|
+
* Get the server's current adoption stage for this tenant.
|
|
674
|
+
* Populated after the first successful heartbeat response.
|
|
675
|
+
* Returns null if not yet received.
|
|
676
|
+
*/
|
|
677
|
+
getAdoptionStage(): string | null;
|
|
665
678
|
}
|
|
666
679
|
|
|
667
680
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1212,6 +1212,12 @@ async function handleSignCommand(command, originalClient, gateClient, options) {
|
|
|
1212
1212
|
if (options.mode === "dry-run") {
|
|
1213
1213
|
return await originalClient.send(new SignCommand(command));
|
|
1214
1214
|
}
|
|
1215
|
+
const GATEWAY_STAGES = ["HARD_KMS_GATEWAY", "HARD_GCP_GATEWAY", "HARD_HSM_GATEWAY"];
|
|
1216
|
+
const currentStage = gateClient.heartbeatManager?.getAdoptionStage?.();
|
|
1217
|
+
if (currentStage && GATEWAY_STAGES.includes(currentStage)) {
|
|
1218
|
+
emitMetric(options.metricsSink, "sign_success_total", labels);
|
|
1219
|
+
return await signViaProxy(gateClient, decision, command, signerId);
|
|
1220
|
+
}
|
|
1215
1221
|
return await originalClient.send(new SignCommand(command));
|
|
1216
1222
|
} catch (error) {
|
|
1217
1223
|
if (error instanceof BlockIntelBlockedError) {
|
|
@@ -1225,6 +1231,72 @@ async function handleSignCommand(command, originalClient, gateClient, options) {
|
|
|
1225
1231
|
throw error;
|
|
1226
1232
|
}
|
|
1227
1233
|
}
|
|
1234
|
+
async function signViaProxy(gateClient, decision, command, signerId) {
|
|
1235
|
+
const config = gateClient.config;
|
|
1236
|
+
const baseUrl = config?.baseUrl || config?.controlPlaneUrl;
|
|
1237
|
+
const tenantId = config?.tenantId;
|
|
1238
|
+
if (!baseUrl || !tenantId) {
|
|
1239
|
+
throw new Error("[Gate SDK] Cannot use signing proxy: baseUrl or tenantId not configured on GateClient");
|
|
1240
|
+
}
|
|
1241
|
+
const message = command.input?.Message ?? command.Message;
|
|
1242
|
+
if (!message) {
|
|
1243
|
+
throw new Error("[Gate SDK] SignCommand missing Message for proxy signing");
|
|
1244
|
+
}
|
|
1245
|
+
const messageBuffer = message instanceof Buffer ? message : Buffer.from(message);
|
|
1246
|
+
const messageBase64 = messageBuffer.toString("base64");
|
|
1247
|
+
const keyId = command.input?.KeyId ?? command.KeyId;
|
|
1248
|
+
if (!keyId) {
|
|
1249
|
+
throw new Error("[Gate SDK] SignCommand missing KeyId for proxy signing");
|
|
1250
|
+
}
|
|
1251
|
+
const signingAlgorithm = command.input?.SigningAlgorithm ?? command.SigningAlgorithm ?? "ECDSA_SHA_256";
|
|
1252
|
+
const messageType = command.input?.MessageType ?? command.MessageType ?? "RAW";
|
|
1253
|
+
const proxyUrl = `${baseUrl.replace("/defense", "")}/tenants/${tenantId}/defense/sign`;
|
|
1254
|
+
const headers = {
|
|
1255
|
+
"Content-Type": "application/json"
|
|
1256
|
+
};
|
|
1257
|
+
const authHeaders = gateClient.getAuthHeaders?.();
|
|
1258
|
+
if (authHeaders) {
|
|
1259
|
+
Object.assign(headers, authHeaders);
|
|
1260
|
+
} else {
|
|
1261
|
+
const auth = config?.auth;
|
|
1262
|
+
if (auth?.mode === "api_key" && auth?.apiKey) {
|
|
1263
|
+
headers["x-api-key"] = auth.apiKey;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
const jwt = gateClient.jwt || gateClient.config?.jwt;
|
|
1267
|
+
if (jwt) {
|
|
1268
|
+
headers["Authorization"] = `Bearer ${jwt}`;
|
|
1269
|
+
}
|
|
1270
|
+
const response = await fetch(proxyUrl, {
|
|
1271
|
+
method: "POST",
|
|
1272
|
+
headers,
|
|
1273
|
+
body: JSON.stringify({
|
|
1274
|
+
requestId: decision.decisionId || decision.requestId,
|
|
1275
|
+
decisionToken: decision.decisionToken,
|
|
1276
|
+
keyId,
|
|
1277
|
+
message: messageBase64,
|
|
1278
|
+
signingAlgorithm,
|
|
1279
|
+
messageType
|
|
1280
|
+
})
|
|
1281
|
+
});
|
|
1282
|
+
if (!response.ok) {
|
|
1283
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
1284
|
+
const code = errorBody?.error?.code || "SIGN_PROXY_FAILED";
|
|
1285
|
+
const msg = errorBody?.error?.message || `Signing proxy returned ${response.status}`;
|
|
1286
|
+
throw new Error(`[Gate SDK] ${code}: ${msg}`);
|
|
1287
|
+
}
|
|
1288
|
+
const result = await response.json();
|
|
1289
|
+
const data = result?.data;
|
|
1290
|
+
if (!data?.signature) {
|
|
1291
|
+
throw new Error("[Gate SDK] Signing proxy returned no signature");
|
|
1292
|
+
}
|
|
1293
|
+
return {
|
|
1294
|
+
Signature: Buffer.from(data.signature, "base64"),
|
|
1295
|
+
KeyId: data.keyId || keyId,
|
|
1296
|
+
SigningAlgorithm: data.signingAlgorithm || signingAlgorithm,
|
|
1297
|
+
$metadata: { httpStatusCode: 200 }
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1228
1300
|
|
|
1229
1301
|
// src/provenance/ProvenanceProvider.ts
|
|
1230
1302
|
var ProvenanceProvider = class {
|
|
@@ -1282,6 +1354,8 @@ var HeartbeatManager = class {
|
|
|
1282
1354
|
started = false;
|
|
1283
1355
|
maxBackoffSeconds = 30;
|
|
1284
1356
|
// Maximum backoff interval
|
|
1357
|
+
/** Server's current adoption stage for this tenant (cached from heartbeat response) */
|
|
1358
|
+
adoptionStage = null;
|
|
1285
1359
|
maxSigners;
|
|
1286
1360
|
signerIdleTtlMs;
|
|
1287
1361
|
localRateLimitMs;
|
|
@@ -1555,6 +1629,9 @@ var HeartbeatManager = class {
|
|
|
1555
1629
|
policyHash: response.data.policyHash
|
|
1556
1630
|
};
|
|
1557
1631
|
entry.consecutiveFailures = 0;
|
|
1632
|
+
if (response.data.adoptionStage != null) {
|
|
1633
|
+
this.adoptionStage = response.data.adoptionStage;
|
|
1634
|
+
}
|
|
1558
1635
|
console.log("[HEARTBEAT] Acquired heartbeat token", {
|
|
1559
1636
|
expiresAt,
|
|
1560
1637
|
signerId,
|
|
@@ -1583,6 +1660,14 @@ var HeartbeatManager = class {
|
|
|
1583
1660
|
getClientInstanceId() {
|
|
1584
1661
|
return this.clientInstanceId;
|
|
1585
1662
|
}
|
|
1663
|
+
/**
|
|
1664
|
+
* Get the server's current adoption stage for this tenant.
|
|
1665
|
+
* Populated after the first successful heartbeat response.
|
|
1666
|
+
* Returns null if not yet received.
|
|
1667
|
+
*/
|
|
1668
|
+
getAdoptionStage() {
|
|
1669
|
+
return this.adoptionStage;
|
|
1670
|
+
}
|
|
1586
1671
|
};
|
|
1587
1672
|
|
|
1588
1673
|
// src/security/IamPermissionRiskChecker.ts
|
|
@@ -1915,6 +2000,8 @@ var GateClient = class {
|
|
|
1915
2000
|
apiKey: heartbeatApiKey
|
|
1916
2001
|
});
|
|
1917
2002
|
this.heartbeatManager.start();
|
|
2003
|
+
this.checkAdoptionStageMismatch().catch(() => {
|
|
2004
|
+
});
|
|
1918
2005
|
}
|
|
1919
2006
|
if (!config.local) {
|
|
1920
2007
|
const enforcementMode = config.enforcementMode || "SOFT";
|
|
@@ -1960,9 +2047,38 @@ var GateClient = class {
|
|
|
1960
2047
|
console.warn("[GATE CLIENT] Async IAM risk check warning:", error instanceof Error ? error.message : String(error));
|
|
1961
2048
|
}
|
|
1962
2049
|
}
|
|
2050
|
+
/**
|
|
2051
|
+
* Warn if the local SDK mode is SHADOW but the server's adoption stage is enforcing.
|
|
2052
|
+
* Runs non-blocking after heartbeat startup; never throws.
|
|
2053
|
+
*/
|
|
2054
|
+
async checkAdoptionStageMismatch() {
|
|
2055
|
+
if (!this.heartbeatManager) return;
|
|
2056
|
+
const signerId = this.config.signerId ?? DEFAULT_SIGNER_ID;
|
|
2057
|
+
try {
|
|
2058
|
+
await this.heartbeatManager.getTokenForSigner(signerId, 5e3);
|
|
2059
|
+
} catch {
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
const adoptionStage = this.heartbeatManager.getAdoptionStage();
|
|
2063
|
+
if (!adoptionStage) return;
|
|
2064
|
+
const ENFORCING_STAGES = [
|
|
2065
|
+
"SOFT_ENFORCE",
|
|
2066
|
+
"HARD_ENFORCE",
|
|
2067
|
+
"PROVENANCE",
|
|
2068
|
+
"HARD_KMS_GATEWAY",
|
|
2069
|
+
"HARD_KMS_ATTESTED",
|
|
2070
|
+
"HARD_KMS_ATTESTED_ENCLAVE",
|
|
2071
|
+
"HARD_GCP_CONFIDENTIAL_VM"
|
|
2072
|
+
];
|
|
2073
|
+
if (this.mode === "SHADOW" && ENFORCING_STAGES.includes(adoptionStage)) {
|
|
2074
|
+
console.warn(
|
|
2075
|
+
`[GATE SDK] Server adoption stage is ${adoptionStage} but SDK mode is SHADOW. Consider updating mode to 'ENFORCE' so your application handles blocks correctly. Until updated, the SDK will allow transactions the server would block.`
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
1963
2079
|
/**
|
|
1964
2080
|
* Evaluate a transaction defense request
|
|
1965
|
-
*
|
|
2081
|
+
*
|
|
1966
2082
|
* Implements:
|
|
1967
2083
|
* - Shadow Mode (SHADOW: monitor-only, ENFORCE: enforce decisions)
|
|
1968
2084
|
* - Connection failure strategy (FAIL_OPEN vs FAIL_CLOSED)
|
|
@@ -2223,7 +2339,8 @@ var GateClient = class {
|
|
|
2223
2339
|
}
|
|
2224
2340
|
}
|
|
2225
2341
|
if (result.decision === "BLOCK") {
|
|
2226
|
-
|
|
2342
|
+
const effectiveMode = result.mode ?? requestMode;
|
|
2343
|
+
if (effectiveMode === "SOFT_ENFORCE") {
|
|
2227
2344
|
console.warn("[SOFT ENFORCE] Policy violation detected - app can override", {
|
|
2228
2345
|
requestId,
|
|
2229
2346
|
reasonCodes: result.reasonCodes
|
|
@@ -2237,7 +2354,7 @@ var GateClient = class {
|
|
|
2237
2354
|
warning: "Policy violation detected. Override at your own risk."
|
|
2238
2355
|
};
|
|
2239
2356
|
}
|
|
2240
|
-
if (
|
|
2357
|
+
if (effectiveMode === "SHADOW") {
|
|
2241
2358
|
console.warn("[GATE SHADOW MODE] Would have blocked transaction", {
|
|
2242
2359
|
requestId,
|
|
2243
2360
|
reasonCodes: result.reasonCodes,
|