forked from wangziqi/gongxue-base
chore: harden production readiness gates
This commit is contained in:
@@ -7,12 +7,18 @@ import { spawnSync } from 'node:child_process';
|
||||
const repoRoot = process.cwd();
|
||||
const scriptPath = path.join(repoRoot, 'scripts', 'production-readiness-check.js');
|
||||
|
||||
function runReadiness(envContent) {
|
||||
function runReadiness(envContent, options = {}) {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tiku-readiness-'));
|
||||
const envFile = path.join(tempDir, '.env');
|
||||
fs.writeFileSync(envFile, envContent, 'utf8');
|
||||
const args = [scriptPath, '--env-file', envFile, '--skip-db', '--json'];
|
||||
if (options.providerRows) {
|
||||
const fixtureFile = path.join(tempDir, 'provider-fixture.json');
|
||||
fs.writeFileSync(fixtureFile, JSON.stringify({ rows: options.providerRows }, null, 2), 'utf8');
|
||||
args.push('--provider-config-fixture', fixtureFile);
|
||||
}
|
||||
|
||||
const result = spawnSync(process.execPath, [scriptPath, '--env-file', envFile, '--skip-db', '--json'], {
|
||||
const result = spawnSync(process.execPath, args, {
|
||||
cwd: repoRoot,
|
||||
encoding: 'utf8',
|
||||
env: {
|
||||
@@ -47,6 +53,37 @@ assert.ok(
|
||||
'unsafe readiness should block mock SMS provider',
|
||||
);
|
||||
|
||||
const unsupportedSmsProvider = runReadiness(`
|
||||
NODE_ENV=production
|
||||
DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku
|
||||
CORS_ORIGIN=https://student.gongxue100.com
|
||||
AUTH_SMS_PROVIDER=aliyun-production
|
||||
AUTH_CODE_PEPPER=s3cure-prod-code-pepper-2026-06-29-abcdef
|
||||
AUTH_SESSION_SECRET=s3cure-prod-session-secret-2026-06-29-ghijkl
|
||||
AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json
|
||||
AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1
|
||||
ALLOW_LEGACY_AUTH_HEADERS=false
|
||||
ALLOW_PLATFORM_ADMIN_KEY=false
|
||||
PLATFORM_ADMIN_API_KEY=s3cure-platform-admin-key-2026-06-29-mnopqr
|
||||
STORAGE_DEFAULT_PROVIDER=aliyun_oss
|
||||
STORAGE_DEFAULT_BUCKET=tiku-assets
|
||||
STORAGE_REQUIRE_TENANT_PREFIX=true
|
||||
ALIYUN_OSS_REGION=cn-hangzhou
|
||||
ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com
|
||||
ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY
|
||||
ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder
|
||||
WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http
|
||||
WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan
|
||||
WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx
|
||||
WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false
|
||||
`);
|
||||
|
||||
assert.notEqual(unsupportedSmsProvider.status, 0, 'unsupported production SMS provider readiness should fail');
|
||||
assert.ok(
|
||||
unsupportedSmsProvider.payload.checks?.some(item => item.id === 'env.auth_sms_provider' && item.status === 'blocker'),
|
||||
'readiness should block unsupported AUTH_SMS_PROVIDER values',
|
||||
);
|
||||
|
||||
const strongSecretA = 's3cure-prod-code-pepper-2026-06-29-abcdef';
|
||||
const strongSecretB = 's3cure-prod-session-secret-2026-06-29-ghijkl';
|
||||
const strongSecretC = 's3cure-platform-admin-key-2026-06-29-mnopqr';
|
||||
@@ -95,6 +132,84 @@ assert.ok(
|
||||
'env-only readiness should explicitly warn that DB checks are skipped',
|
||||
);
|
||||
|
||||
const unsafeProviderFixture = runReadiness(
|
||||
`
|
||||
NODE_ENV=production
|
||||
DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku
|
||||
CORS_ORIGIN=https://student.gongxue100.com,https://tenant-admin.gongxue100.com,https://platform-admin.gongxue100.com
|
||||
AUTH_SMS_PROVIDER=aliyun
|
||||
AUTH_CODE_PEPPER=${strongSecretA}
|
||||
AUTH_SESSION_SECRET=${strongSecretB}
|
||||
AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json
|
||||
AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1
|
||||
ALLOW_LEGACY_AUTH_HEADERS=false
|
||||
ALLOW_PLATFORM_ADMIN_KEY=false
|
||||
PLATFORM_ADMIN_API_KEY=${strongSecretC}
|
||||
STORAGE_DEFAULT_PROVIDER=aliyun_oss
|
||||
STORAGE_DEFAULT_BUCKET=tiku-assets
|
||||
STORAGE_REQUIRE_TENANT_PREFIX=true
|
||||
ALIYUN_OSS_REGION=cn-hangzhou
|
||||
ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com
|
||||
ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY
|
||||
ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder
|
||||
WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http
|
||||
WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan
|
||||
WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx
|
||||
WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false
|
||||
`,
|
||||
{
|
||||
providerRows: [
|
||||
{
|
||||
source: 'auth',
|
||||
tenantId: 'tenant-a',
|
||||
provider: 'qq-oauth',
|
||||
configPublic: {
|
||||
appId: '101000000',
|
||||
redirectUri: 'http://localhost:5173/auth/qq/callback',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: 'payment',
|
||||
tenantId: 'tenant-a',
|
||||
provider: 'wechat_pay',
|
||||
configPublic: {
|
||||
appId: 'wx123',
|
||||
merchantId: '1900000001',
|
||||
merchantSerialNo: 'serial123',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: 'payment',
|
||||
tenantId: 'tenant-b',
|
||||
provider: 'alipay',
|
||||
configPublic: {
|
||||
appId: '2021000000000000',
|
||||
notifyUrl: 'http://pay.example.com/notify',
|
||||
privateKey: 'should-not-be-public',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
assert.notEqual(unsafeProviderFixture.status, 0, 'unsafe provider fixture readiness should fail');
|
||||
assert.ok(
|
||||
unsafeProviderFixture.payload.checks?.some(item => item.id === 'db.auth.qq-oauth.redirect_uri.unsafe' && item.status === 'blocker'),
|
||||
'readiness should block unsafe QQ redirectUri',
|
||||
);
|
||||
assert.ok(
|
||||
unsafeProviderFixture.payload.checks?.some(item => item.id === 'db.payment.wechat_pay.public_required' && item.status === 'blocker'),
|
||||
'readiness should block missing WeChat Pay notifyUrl',
|
||||
);
|
||||
assert.ok(
|
||||
unsafeProviderFixture.payload.checks?.some(item => item.id === 'db.payment.alipay.notify_url.unsafe' && item.status === 'blocker'),
|
||||
'readiness should block unsafe Alipay notifyUrl',
|
||||
);
|
||||
assert.ok(
|
||||
unsafeProviderFixture.payload.checks?.some(item => item.id === 'db.payment.alipay.public_secret' && item.status === 'blocker'),
|
||||
'readiness should block secret-like public payment config keys',
|
||||
);
|
||||
|
||||
const missingJwksIssuer = runReadiness(`
|
||||
NODE_ENV=production
|
||||
DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku
|
||||
|
||||
Reference in New Issue
Block a user