one
Version:
One is a new React Framework that makes Vite serve both native and web.
155 lines • 5.62 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 = [
// api tokens / keys with known prefixes
["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],
// known env var names that should never be inlined
["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",
// WebRTC SDP
"4ad15a19-80e2-4105-bf43-48039fd2963e"
// WebRTC SDP
]);
const BUILTIN_SAFE_PATTERNS = [/sk_live_your_/, /sk_live_your_key/, /rk_\w+_\w+/,
// tamagui theme tokens
/sk_all_element/,
// DOM property names
/sk_personal_data/
// analytics property names
];
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" ? "\u{1F6A8}" : "\u26A0\uFE0F";
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;
}