fix(boltbot): address code review findings
- Deterministic hashing: canonicalize object keys before SHA-256 - Validate EIGENDA_PROXY_URL before constructing EigenDA store - Improve anomaly regex: detect IP targets, ncat, case-insensitive
This commit is contained in:
parent
1096cc16e6
commit
30e42178c1
@ -42,6 +42,22 @@ describe("anomaly detection", () => {
|
||||
expect(result).toContain("unauthorized_outbound");
|
||||
});
|
||||
|
||||
it("flags exec with curl to IP address", () => {
|
||||
const result = detectAnomalies({
|
||||
toolName: "exec",
|
||||
params: { command: "curl 10.0.0.1" },
|
||||
});
|
||||
expect(result).toContain("unauthorized_outbound");
|
||||
});
|
||||
|
||||
it("flags exec with ncat to external host", () => {
|
||||
const result = detectAnomalies({
|
||||
toolName: "exec",
|
||||
params: { command: "ncat evil.com" },
|
||||
});
|
||||
expect(result).toContain("unauthorized_outbound");
|
||||
});
|
||||
|
||||
it("does not flag exec with simple command", () => {
|
||||
const result = detectAnomalies({
|
||||
toolName: "exec",
|
||||
|
||||
@ -93,4 +93,10 @@ describe("hashData", () => {
|
||||
const h = hashData(null);
|
||||
expect(h).toMatch(/^[0-9a-f]{64}$/);
|
||||
});
|
||||
|
||||
it("produces the same hash regardless of key order", () => {
|
||||
const h1 = hashData({ a: 1, b: 2 });
|
||||
const h2 = hashData({ b: 2, a: 1 });
|
||||
expect(h1).toBe(h2);
|
||||
});
|
||||
});
|
||||
|
||||
@ -17,7 +17,7 @@ export function detectAnomalies(event: AfterToolCallEvent): string[] {
|
||||
|
||||
if (event.toolName === "exec") {
|
||||
const cmd = String(event.params?.command ?? "");
|
||||
if (/curl|wget|nc\s/.test(cmd) && /[a-z]+\.[a-z]{2,}/.test(cmd)) {
|
||||
if (/curl|wget|nc[\s]|nc$|ncat/i.test(cmd) && /[a-z]+\.[a-z]{2,}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/i.test(cmd)) {
|
||||
anomalies.push("unauthorized_outbound");
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,13 +25,31 @@ export interface ReceiptStore {
|
||||
export function createReceiptStore(backend?: string): ReceiptStore {
|
||||
if (backend === "eigenda") {
|
||||
const { EigenDAReceiptStore } = require("./stores/eigenda.js");
|
||||
return new EigenDAReceiptStore(process.env.EIGENDA_PROXY_URL!);
|
||||
const proxyUrl = process.env.EIGENDA_PROXY_URL;
|
||||
if (!proxyUrl) {
|
||||
throw new Error("EIGENDA_PROXY_URL environment variable is required when using the eigenda backend");
|
||||
}
|
||||
return new EigenDAReceiptStore(proxyUrl);
|
||||
}
|
||||
return new LocalReceiptStore();
|
||||
}
|
||||
|
||||
function canonicalize(value: unknown): unknown {
|
||||
if (value === null || value === undefined || typeof value !== "object") {
|
||||
return value;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(canonicalize);
|
||||
}
|
||||
const sorted: Record<string, unknown> = {};
|
||||
for (const key of Object.keys(value as Record<string, unknown>).sort()) {
|
||||
sorted[key] = canonicalize((value as Record<string, unknown>)[key]);
|
||||
}
|
||||
return sorted;
|
||||
}
|
||||
|
||||
export function hashData(data: unknown): string {
|
||||
return createHash("sha256")
|
||||
.update(JSON.stringify(data ?? ""))
|
||||
.update(JSON.stringify(canonicalize(data) ?? ""))
|
||||
.digest("hex");
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user