feat(preflight): add exploit-readiness probe and --validate-auth mode, and refresh suggested models (#476)

* feat(preflight): gate scans on an exploit-workload readiness probe

* feat: add --validate-auth to run authentication validation only

* feat: refuse reusing an auth-validation workspace for a scan

* chore: refresh suggested model IDs (Grok 4.7, OpenAI gpt-6-sol, Claude 5)

* fix(preflight): make the exploit-readiness probe trip the cyber safeguard reliably

* chore(preflight): update the exploit-readiness probe prompt

* feat: add --validate-model to run the preflight model checks only

* feat(cli): name cyber-access and app-login steps in the start loader

* feat(cli): refine start loader — skip app-login step when following, annotate preflight label

* feat(status): show Preflight and Cyber access verification rows for gated providers

* chore(preflight): suggest a fallback model in cyber-access remediation hints

* chore(preflight): drop env-var syntax from cyber-access fallback hints

* fix(preflight): separate finding heading from Target line in readiness probe

* refactor(preflight): rename exploit-readiness probe to cyber access verification

* fix(preflight): re-join finding heading with Target line in readiness probe

Reverts the heading/Target split from 22d84f2, gluing each finding's
heading back onto its Target line in the cyber-access probe's user
content.

* feat(validation): show a Checks summary in the validation log

* fix(cli): say a validation run failed, not that it could not start

* docs(ai-providers): replace broken Pi subscription link with /login steps

* feat(preflight): gate the openai-codex subscription on cyber access
This commit is contained in:
ezl-keygraph authored and GitHub committed 2026-10-05 23:58:14 +05:30
1 parent a14c7944d8
commit 0ab7c0b41b
26 files changed
+759 -73

No files matched your search

+46 -2
View File
@@ -100,6 +100,8 @@ const PRODUCTION_RETRY = {
'InvalidTargetError',
'AuthLoginFailedError',
'PermanentError',
'OpenAiCyberAccessError',
'AnthropicCyberAccessError',
],
};
@@ -379,11 +381,15 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
const { workflowId } = workflowInfo();
const a = input.pipelineTestingMode ? testActs : acts;
const exploit = input.exploit ?? true;
const authOnly = input.authOnly ?? false;
const validateModel = input.validateModel ?? false;
const sessionId = input.sessionId || input.resumeFromWorkspace || workflowId;
const stateContext: 'fresh' | 'resume' = input.resumeFromWorkspace ? 'resume' : 'fresh';
const state: PipelineState = {
status: 'running',
authOnly,
validateModel,
currentPhase: null,
currentAgent: null,
completedAgents: [],
@@ -1287,7 +1293,7 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
const durable = await deterministicReportActs.initializeDurableScanState(activityInput, exploit, stateContext);
applyDurableSummary(durable);
if (input.resumeFromWorkspace) {
if (!authOnly && input.resumeFromWorkspace) {
// The new workflow id lands in session.json before anything that can reject the resume, so a
// validation or checkpoint-restore failure still leaves the CLI an attempt to follow.
await deterministicReportActs.registerResumeAttempt(activityInput, input.terminatedWorkflows ?? []);
@@ -1337,7 +1343,30 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
state.currentPhase = 'preflight';
state.currentAgent = null;
await preflightActs.runPreflightValidation(activityInput);
await runOperation('preflight', 'Preflight', () => preflightActs.runPreflightValidation(activityInput));
if (!authOnly) {
const startedAt = startOperation('cyber-access', 'Cyber access verification');
try {
const verification = await preflightActs.runCyberAccessVerification(activityInput);
if (verification.gated) {
completeOperation('cyber-access', 'Cyber access verification', startedAt);
} else {
delete state.operationalStages['cyber-access'];
}
} catch (error) {
failOperation('cyber-access', 'Cyber access verification', startedAt);
throw error;
}
}
if (validateModel) {
state.status = 'completed';
state.currentPhase = null;
state.summary = computeSummary(state, usageAccountingComplete());
await a.logWorkflowComplete(activityInput, toWorkflowSummary(state, 'completed'));
return state;
}
await preflightActs.syncPlaywrightStealthConfig(activityInput);
state.currentPhase = 'auth-validation';
@@ -1346,6 +1375,21 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
if (authMetrics !== null) state.agentMetrics['validate-authentication'] = authMetrics;
state.currentAgent = null;
// Auth-only runs stop here; a null result means no authentication block, which is a misconfig.
if (authOnly) {
if (authMetrics === null) {
throw ApplicationFailure.nonRetryable(
'An auth-validation run needs an authentication block in the config. Add one, or drop --validate-auth.',
'ConfigurationError',
);
}
state.status = 'completed';
state.currentPhase = null;
state.summary = computeSummary(state, usageAccountingComplete());
await a.logWorkflowComplete(activityInput, toWorkflowSummary(state, 'completed'));
return state;
}
await a.initDeliverableGit(activityInput);
await a.syncCodePathDenyRules(activityInput);