diff --git a/nemoclaw/src/security/secret-scanner.test.ts b/nemoclaw/src/security/secret-scanner.test.ts index e1394600b3f..0c088c2038e 100644 --- a/nemoclaw/src/security/secret-scanner.test.ts +++ b/nemoclaw/src/security/secret-scanner.test.ts @@ -9,9 +9,11 @@ import { isMemoryPath, scanForSecrets } from "./secret-scanner.js"; const FAKE = { nvidia: "nvapi-" + "abcdefghijklmnopqrstuvwxyz", openai: "sk-" + "abc123def456ghi789jkl012mno", + openaiProject: "sk-proj-" + "abc123_def456-ghi789_jkl012-mno345", github: "ghp_" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn", aws: "AKIA" + "IOSFODNN7EXAMPLE", slack: "xoxb-" + "123456789-abcdefghij", + slackApp: "xapp-" + "1-A0000-12345-abcdef", npm: "npm_" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn", pemRsa: "-----BEGIN RSA " + "PRIVATE KEY-----\nMIIEpA...", pemOpenssh: "-----BEGIN OPENSSH " + "PRIVATE KEY-----\nb3Blbn...", @@ -40,6 +42,12 @@ describe("scanForSecrets", () => { expect(matches[0].pattern).toBe("OpenAI API key"); }); + it("OpenAI project API key", () => { + const matches = scanForSecrets(`export OPENAI_API_KEY=${FAKE.openaiProject}`); + expect(matches).toHaveLength(1); + expect(matches[0].pattern).toBe("OpenAI API key"); + }); + it("GitHub personal access token", () => { const matches = scanForSecrets(`token: ${FAKE.github}`); expect(matches).toHaveLength(1); @@ -58,6 +66,12 @@ describe("scanForSecrets", () => { expect(matches[0].pattern).toBe("Slack token"); }); + it("Slack app token", () => { + const matches = scanForSecrets(`SLACK_APP_TOKEN=${FAKE.slackApp}`); + expect(matches).toHaveLength(1); + expect(matches[0].pattern).toBe("Slack token"); + }); + it("npm token", () => { const matches = scanForSecrets(`//registry.npmjs.org/:_authToken=${FAKE.npm}`); expect(matches).toHaveLength(1); diff --git a/nemoclaw/src/security/secret-scanner.ts b/nemoclaw/src/security/secret-scanner.ts index 90742190f99..5e543702743 100644 --- a/nemoclaw/src/security/secret-scanner.ts +++ b/nemoclaw/src/security/secret-scanner.ts @@ -26,7 +26,7 @@ const SECRET_PATTERNS: SecretPattern[] = [ { name: "NVIDIA API key", regex: /\bnvapi-[A-Za-z0-9_-]{20,}\b/ }, // OpenAI — exclude sk-ant- (Anthropic) to avoid double-matching - { name: "OpenAI API key", regex: /\bsk-(?!ant-)[A-Za-z0-9]{20,}\b/ }, + { name: "OpenAI API key", regex: /\bsk-(?!ant-)[A-Za-z0-9_-]{20,}\b/ }, // GitHub { name: "GitHub token", regex: /\b(ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9]{36,}\b/ }, @@ -39,7 +39,7 @@ const SECRET_PATTERNS: SecretPattern[] = [ }, // Slack - { name: "Slack token", regex: /\bxox[bpas]-[A-Za-z0-9-]{10,}\b/ }, + { name: "Slack token", regex: /\b(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}\b/ }, // Discord — require contextual prefix to avoid matching JWT/base64 strings {