one
Version:
One is a React framework for web, iOS, and Android apps.
159 lines (158 loc) • 5.21 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all) __defProp(target, name, {
get: all[name],
enumerable: true
});
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: () => from[key],
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var securityScan_exports = {};
__export(securityScan_exports, {
runSecurityScan: () => runSecurityScan,
scanBundleForSecrets: () => scanBundleForSecrets
});
module.exports = __toCommonJS(securityScan_exports);
var import_promises = require("node:fs/promises");
var import_node_path = require("node:path");
const SECRET_PATTERNS = [
["Anthropic API key", /sk-ant-[A-Za-z0-9_-]{20,}/g],
["OpenAI API key", /sk-proj-[A-Za-z0-9_-]{20,}/g],
["Stripe secret key", /sk_live_[A-Za-z0-9]{20,}/g],
["Stripe webhook secret", /whsec_[A-Za-z0-9]{20,}/g],
["GitHub token", /gh[ps]_[A-Za-z0-9]{36,}/g],
["GitHub PAT", /github_pat_[A-Za-z0-9_]{20,}/g],
["AWS access key", /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g],
["Postmark server token", /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g],
["Bearer token assignment", /["']Bearer\s+[A-Za-z0-9_.-]{20,}["']/g],
["Generic secret assignment", /(?:secret|password|api_key|apikey)\s*[:=]\s*["'][A-Za-z0-9_-]{16,}["']/gi],
["BETTER_AUTH_SECRET value", /BETTER_AUTH_SECRET["']\s*[:=]\s*["'][^"']+["']/g],
["POSTMARK_SERVER_TOKEN value", /POSTMARK_SERVER_TOKEN["']\s*[:=]\s*["'][^"']+["']/g],
["ANTHROPIC_API_KEY value", /ANTHROPIC_API_KEY["']\s*[:=]\s*["'][^"']+["']/g]
];
const SAFE_UUIDS = /* @__PURE__ */ new Set([
"00000000-0000-0000-0000-000000000000",
"09259e3b-7be8-46f6-9801-106bf1866e1c",
"4ad15a19-80e2-4105-bf43-48039fd2963e"
]);
const BUILTIN_SAFE_PATTERNS = [
/sk_live_your_/,
/sk_live_your_key/,
/rk_\w+_\w+/,
/sk_all_element/,
/sk_personal_data/
];
function createSafeMatcher(userPatterns) {
const safeStrings = new Set(SAFE_UUIDS);
const safeRegexes = [...BUILTIN_SAFE_PATTERNS];
if (userPatterns) {
for (const p of userPatterns) {
if (typeof p === "string") {
safeStrings.add(p);
} else {
safeRegexes.push(p);
}
}
}
return (match) => {
if (safeStrings.has(match)) return true;
return safeRegexes.some((p) => p.test(match));
};
}
async function collectJSFiles(dir) {
const files = [];
async function walk(currentDir) {
let entries;
try {
entries = await (0, import_promises.readdir)(currentDir, { withFileTypes: true });
} catch {
return;
}
for (const entry of entries) {
const fullPath = (0, import_node_path.join)(currentDir, entry.name);
if (entry.isDirectory()) {
await walk(fullPath);
} else if (entry.name.endsWith(".js")) {
files.push(fullPath);
}
}
}
await walk(dir);
return files;
}
async function scanBundleForSecrets(distDir, userSafePatterns) {
const isSafe = createSafeMatcher(userSafePatterns);
const files = await collectJSFiles(distDir);
const findings = [];
for (const fullPath of files) {
const relativePath = fullPath.slice(distDir.length + 1);
let content;
try {
content = await (0, import_promises.readFile)(fullPath, "utf-8");
} catch {
continue;
}
for (const [label, pattern] of SECRET_PATTERNS) {
pattern.lastIndex = 0;
let match;
while ((match = pattern.exec(content)) !== null) {
const matched = match[0];
if (isSafe(matched)) continue;
const beforeMatch = content.slice(0, match.index);
const line = (beforeMatch.match(/\n/g)?.length ?? 0) + 1;
findings.push({
file: relativePath,
label,
match: matched.length > 40 ? `${matched.slice(0, 20)}...${matched.slice(-10)}` : matched,
line
});
}
}
}
return {
clean: findings.length === 0,
findings
};
}
async function runSecurityScan(clientDir, level, safePatterns) {
console.info(`
\u{1F512} scanning client bundles for leaked secrets...
`);
const { clean, findings } = await scanBundleForSecrets(clientDir, safePatterns);
if (clean) {
console.info(`
\u{1F512} security scan passed \u2014 no secrets found
`);
return true;
}
const icon = level === "error" ? "🚨" : "⚠️";
const header = level === "error" ? `${icon} ${findings.length} secret(s) leaked into client bundle:` : `${icon} ${findings.length} potential secret(s) found in client bundle:`;
console.error(`
${header}
`);
for (const f of findings) {
console.error(` ${f.label}`);
console.error(` file: ${f.file}:${f.line}`);
console.error(` match: ${f.match}
`);
}
if (level === "error") {
console.error(` Set build.securityScan to 'warn' to continue building despite findings.
`);
return false;
}
console.warn(` Set build.securityScan to 'error' to fail builds when secrets are detected.
`);
return true;
}