forked from wangziqi/gongxue-base
feat: add external asset security scanner
This commit is contained in:
@@ -267,6 +267,51 @@ function validateEnv() {
|
||||
warn('env.storage_octet_stream', 'application/octet-stream is allowed; consider removing it after import migration is stable');
|
||||
}
|
||||
|
||||
const scannerModes = envList('WORKER_ASSET_SECURITY_SCANNER', 'metadata_rules').map(item => item.toLowerCase());
|
||||
const unsupportedScannerModes = scannerModes.filter(mode => mode !== 'metadata_rules' && mode !== 'http');
|
||||
if (scannerModes.length === 0) {
|
||||
block('env.asset_security_scanner.empty', 'WORKER_ASSET_SECURITY_SCANNER must include metadata_rules and a production scanner');
|
||||
} else if (unsupportedScannerModes.length > 0) {
|
||||
block('env.asset_security_scanner.unsupported', 'WORKER_ASSET_SECURITY_SCANNER contains unsupported modes', {
|
||||
modes: unsupportedScannerModes,
|
||||
});
|
||||
} else if (!scannerModes.includes('http')) {
|
||||
block('env.asset_security_scanner.external', 'Production assets require WORKER_ASSET_SECURITY_SCANNER=http or metadata_rules,http');
|
||||
} else {
|
||||
pass('env.asset_security_scanner', 'external asset security scanner is enabled', { modes: scannerModes });
|
||||
}
|
||||
|
||||
const scannerEndpoint = env('WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT', '');
|
||||
if (scannerModes.includes('http')) {
|
||||
const scannerHost = hostFromUrl(scannerEndpoint);
|
||||
if (!scannerEndpoint) {
|
||||
block('env.asset_security_scan_http_endpoint', 'WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT is required when http scanner is enabled');
|
||||
} else if (!scannerEndpoint.startsWith('https://') || isLocalHost(scannerHost)) {
|
||||
block('env.asset_security_scan_http_endpoint.unsafe', 'WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT must be a production HTTPS URL');
|
||||
} else {
|
||||
pass('env.asset_security_scan_http_endpoint', 'asset security scanner endpoint is HTTPS');
|
||||
}
|
||||
|
||||
if (isUnsafeSecret(env('WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN', ''), '')) {
|
||||
block('env.asset_security_scan_http_token', 'WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN must be a strong shared secret or service token');
|
||||
} else {
|
||||
pass('env.asset_security_scan_http_token', 'asset security scanner token looks production-grade');
|
||||
}
|
||||
}
|
||||
|
||||
const scannerTimeout = envNumber('WORKER_ASSET_SECURITY_SCAN_HTTP_TIMEOUT_MS', 10_000);
|
||||
if (scannerTimeout <= 0 || scannerTimeout > 60_000) {
|
||||
block('env.asset_security_scan_http_timeout', 'WORKER_ASSET_SECURITY_SCAN_HTTP_TIMEOUT_MS must be between 1 and 60000');
|
||||
} else {
|
||||
pass('env.asset_security_scan_http_timeout', 'asset security scanner timeout is bounded');
|
||||
}
|
||||
|
||||
if (envBool('WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN', false)) {
|
||||
block('env.asset_security_scan_fail_open', 'WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN must be false in production');
|
||||
} else {
|
||||
pass('env.asset_security_scan_fail_open', 'asset security scanning fails closed');
|
||||
}
|
||||
|
||||
if (envBool('WORKER_CRM_ALLOW_INSECURE_LOCALHOST', false)) {
|
||||
block('env.worker_crm_insecure_localhost', 'WORKER_CRM_ALLOW_INSECURE_LOCALHOST must be false in production');
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user