test: 加固NapCat上游审计Jenkins门禁

This commit is contained in:
sunlei 2026-06-25 04:03:21 +08:00
parent f9ce9b6015
commit 1abfcbf59d

View File

@ -470,29 +470,70 @@ export async function runSelfTest(): Promise<void> {
throw new Error(`NapCat upstream Jenkins template missing: ${expected}`); throw new Error(`NapCat upstream Jenkins template missing: ${expected}`);
} }
} }
const defaultUpstreamAuditIndex = upstreamSyncJenkinsTemplate.indexOf( /**
"run napcat-upstream-audit --", * Verifies that the default upstream audit Jenkins call stays dry-run before the optional Codex execution stage.
* @param template - Full Jenkinsfile text to inspect.
* @returns Nothing; throws when the default audit call is unsafe.
*/
const assertUpstreamJenkinsDefaultAuditDryRun = (template: string): void => {
const defaultStageStart = template.indexOf(
"stage('ktWorkflow upstream audit dry-run')",
);
const codexStageStart = template.indexOf(
"stage('ktWorkflow upstream audit with Codex')",
); );
const firstUpstreamExecuteIndex =
upstreamSyncJenkinsTemplate.indexOf("--execute");
if ( if (
defaultUpstreamAuditIndex === -1 || defaultStageStart === -1 ||
(firstUpstreamExecuteIndex !== -1 && codexStageStart === -1 ||
defaultUpstreamAuditIndex > firstUpstreamExecuteIndex) defaultStageStart >= codexStageStart
) { ) {
throw new Error( throw new Error(
"NapCat upstream Jenkins template must run dry-run audit before execute", "NapCat upstream Jenkins template default audit stage order self-check failed",
); );
} }
const defaultUpstreamAuditCall = upstreamSyncJenkinsTemplate.slice( const defaultUpstreamAuditStage = template.slice(
defaultUpstreamAuditIndex, defaultStageStart,
firstUpstreamExecuteIndex === -1 codexStageStart,
? upstreamSyncJenkinsTemplate.length
: firstUpstreamExecuteIndex,
); );
if (defaultUpstreamAuditCall.includes("--use-codex")) { if (!defaultUpstreamAuditStage.includes("run napcat-upstream-audit --")) {
throw new Error( throw new Error(
"NapCat upstream Jenkins template default audit must not use Codex", "NapCat upstream Jenkins template default audit stage missing audit command",
);
}
for (const forbiddenFlag of ["--execute", "--use-codex"]) {
if (defaultUpstreamAuditStage.includes(forbiddenFlag)) {
throw new Error(
`NapCat upstream Jenkins template default audit must not include ${forbiddenFlag}`,
);
}
}
};
assertUpstreamJenkinsDefaultAuditDryRun(upstreamSyncJenkinsTemplate);
const unsafeDefaultUpstreamAuditTemplate = [
"stage('ktWorkflow upstream audit dry-run') {",
" pnpm --dir \"$KT_WORKFLOW_DIR\" run napcat-upstream-audit -- \\",
" --execute \\",
" --use-codex",
"}",
"stage('ktWorkflow upstream audit with Codex') {",
" pnpm --dir \"$KT_WORKFLOW_DIR\" run napcat-upstream-audit -- \\",
" --execute \\",
" --use-codex",
"}",
].join("\n");
let unsafeDefaultUpstreamAuditRejected = false;
try {
assertUpstreamJenkinsDefaultAuditDryRun(
unsafeDefaultUpstreamAuditTemplate,
);
} catch (error) {
unsafeDefaultUpstreamAuditRejected = String(error).includes(
"NapCat upstream Jenkins template default audit must not include",
);
}
if (!unsafeDefaultUpstreamAuditRejected) {
throw new Error(
"NapCat upstream Jenkins template unsafe default audit self-check failed",
); );
} }
for (const forbidden of ["git merge", "git push", "docker build", "kubectl"]) { for (const forbidden of ["git merge", "git push", "docker build", "kubectl"]) {
@ -517,6 +558,8 @@ export async function runSelfTest(): Promise<void> {
"string(name: 'API_PROMOTION_JOB'", "string(name: 'API_PROMOTION_JOB'",
"git merge", "git merge",
"git push", "git push",
"docker build",
"kubectl",
]) { ]) {
if (runtimeReleaseJenkinsTemplate.includes(forbidden)) { if (runtimeReleaseJenkinsTemplate.includes(forbidden)) {
throw new Error( throw new Error(