UNPKG

one

Version:

One is a React framework for web, iOS, and Android apps.

1 lines • 7.43 kB
{"version":3,"file":"securityScan.js","names":[],"sources":["esm/cli/securityScan.js"],"sourcesContent":["import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nconst SECRET_PATTERNS = [\n // api tokens / keys with known prefixes\n [\"Anthropic API key\", /sk-ant-[A-Za-z0-9_-]{20,}/g],\n [\"OpenAI API key\", /sk-proj-[A-Za-z0-9_-]{20,}/g],\n [\"Stripe secret key\", /sk_live_[A-Za-z0-9]{20,}/g],\n [\"Stripe webhook secret\", /whsec_[A-Za-z0-9]{20,}/g],\n [\"GitHub token\", /gh[ps]_[A-Za-z0-9]{36,}/g],\n [\"GitHub PAT\", /github_pat_[A-Za-z0-9_]{20,}/g],\n [\"AWS access key\", /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g],\n [\n \"Postmark server token\",\n /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g\n ],\n [\"Bearer token assignment\", /[\"']Bearer\\s+[A-Za-z0-9_.-]{20,}[\"']/g],\n [\n \"Generic secret assignment\",\n /(?:secret|password|api_key|apikey)\\s*[:=]\\s*[\"'][A-Za-z0-9_-]{16,}[\"']/gi\n ],\n // known env var names that should never be inlined\n [\"BETTER_AUTH_SECRET value\", /BETTER_AUTH_SECRET[\"']\\s*[:=]\\s*[\"'][^\"']+[\"']/g],\n [\"POSTMARK_SERVER_TOKEN value\", /POSTMARK_SERVER_TOKEN[\"']\\s*[:=]\\s*[\"'][^\"']+[\"']/g],\n [\"ANTHROPIC_API_KEY value\", /ANTHROPIC_API_KEY[\"']\\s*[:=]\\s*[\"'][^\"']+[\"']/g]\n];\nconst SAFE_UUIDS = /* @__PURE__ */ new Set([\n \"00000000-0000-0000-0000-000000000000\",\n \"09259e3b-7be8-46f6-9801-106bf1866e1c\",\n // WebRTC SDP\n \"4ad15a19-80e2-4105-bf43-48039fd2963e\"\n // WebRTC SDP\n]);\nconst BUILTIN_SAFE_PATTERNS = [\n /sk_live_your_/,\n /sk_live_your_key/,\n /rk_\\w+_\\w+/,\n // tamagui theme tokens\n /sk_all_element/,\n // DOM property names\n /sk_personal_data/\n // analytics property names\n];\nfunction createSafeMatcher(userPatterns) {\n const safeStrings = new Set(SAFE_UUIDS);\n const safeRegexes = [...BUILTIN_SAFE_PATTERNS];\n if (userPatterns) {\n for (const p of userPatterns) {\n if (typeof p === \"string\") {\n safeStrings.add(p);\n } else {\n safeRegexes.push(p);\n }\n }\n }\n return (match) => {\n if (safeStrings.has(match)) return true;\n return safeRegexes.some((p) => p.test(match));\n };\n}\nasync function collectJSFiles(dir) {\n const files = [];\n async function walk(currentDir) {\n let entries;\n try {\n entries = await readdir(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.name.endsWith(\".js\")) {\n files.push(fullPath);\n }\n }\n }\n await walk(dir);\n return files;\n}\nasync function scanBundleForSecrets(distDir, userSafePatterns) {\n const isSafe = createSafeMatcher(userSafePatterns);\n const files = await collectJSFiles(distDir);\n const findings = [];\n for (const fullPath of files) {\n const relativePath = fullPath.slice(distDir.length + 1);\n let content;\n try {\n content = await readFile(fullPath, \"utf-8\");\n } catch {\n continue;\n }\n for (const [label, pattern] of SECRET_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(content)) !== null) {\n const matched = match[0];\n if (isSafe(matched)) continue;\n const beforeMatch = content.slice(0, match.index);\n const line = (beforeMatch.match(/\\n/g)?.length ?? 0) + 1;\n findings.push({\n file: relativePath,\n label,\n match: matched.length > 40 ? `${matched.slice(0, 20)}...${matched.slice(-10)}` : matched,\n line\n });\n }\n }\n }\n return { clean: findings.length === 0, findings };\n}\nasync function runSecurityScan(clientDir, level, safePatterns) {\n console.info(`\n \\u{1F512} scanning client bundles for leaked secrets...\n`);\n const { clean, findings } = await scanBundleForSecrets(clientDir, safePatterns);\n if (clean) {\n console.info(`\n \\u{1F512} security scan passed \\u2014 no secrets found\n`);\n return true;\n }\n const icon = level === \"error\" ? \"\\u{1F6A8}\" : \"\\u26A0\\uFE0F\";\n const header = level === \"error\" ? `${icon} ${findings.length} secret(s) leaked into client bundle:` : `${icon} ${findings.length} potential secret(s) found in client bundle:`;\n console.error(`\n ${header}\n`);\n for (const f of findings) {\n console.error(` ${f.label}`);\n console.error(` file: ${f.file}:${f.line}`);\n console.error(` match: ${f.match}\n`);\n }\n if (level === \"error\") {\n console.error(\n ` Set build.securityScan to 'warn' to continue building despite findings.\n`\n );\n return false;\n }\n console.warn(\n ` Set build.securityScan to 'error' to fail builds when secrets are detected.\n`\n );\n return true;\n}\nexport {\n runSecurityScan,\n scanBundleForSecrets\n};\n//# sourceMappingURL=securityScan.js.map\n"],"mappings":"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"}