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

+30 -2
View File
@@ -363,6 +363,33 @@ function agenticSastPhase(operations: readonly DerivedAgent[]): DerivedPhase | u
};
}
/** Preflight rows shown at the top of the tree, in run order. Each is its own single-line phase. */
const PREFLIGHT_ROW_KEYS = ['preflight', 'cyber-access'] as const;
/**
* The two preflight gates the worker persists — the preflight checks and the cyber-access verification —
* as top-of-tree rows. Each appears once its stage is recorded (running, then done or failed); a
* run that never reaches a gate simply omits its row.
*/
function preflightPhases(operations: readonly DerivedAgent[]): DerivedPhase[] {
const byKey = new Map(operations.map((operation) => [operation.name, operation]));
const phases: DerivedPhase[] = [];
for (const key of PREFLIGHT_ROW_KEYS) {
const operation = byKey.get(key);
if (operation === undefined) continue;
phases.push({
key: operation.name,
label: operation.label,
children: false,
meta: 'duration',
state: operation.state,
summary: operation,
agents: [operation],
});
}
return phases;
}
/**
* Bookkeeping rows worth showing. A deterministic stage that has completed says nothing —
* it can only ever read 0s — but one that is still running, or that failed, is exactly what
@@ -408,13 +435,14 @@ function assemblePhases(agentPhases: readonly DerivedPhase[], operations: readon
return phase;
});
const preflight = preflightPhases(operations);
const sast = agenticSastPhase(operations);
if (sast === undefined) return phases;
if (sast === undefined) return [...preflight, ...phases];
// Agentic SAST starts with the scan and runs alongside the pentest, so it reads after
// the login check rather than appended past Reporting where it never ran.
const afterAuth = phases.findIndex((phase) => phase.key === 'auth-validation') + 1;
return [...phases.slice(0, afterAuth), sast, ...phases.slice(afterAuth)];
return [...preflight, ...phases.slice(0, afterAuth), sast, ...phases.slice(afterAuth)];
}
export { agentError };
+6 -1
View File
@@ -108,6 +108,8 @@ const MISCELLANEOUS_EXPLOIT_AGENT: AgentSpec = {
* available guess.
*/
export function pipelineForState(state: PipelineState | null): readonly PhaseSpec[] {
if (state?.validateModel === true) return [];
if (state?.authOnly === true) return PIPELINE.filter((phase) => phase.key === 'auth-validation');
if (state?.expectedAgents === undefined) return PIPELINE;
const expected = new Set(state.expectedAgents);
return PIPELINE.map((phase) => {
@@ -136,7 +138,8 @@ const AGENTIC_SAST_PARENT_KEY = 'agentic-sast';
// apps/worker/src/temporal/reconcile-activity-types.ts, and
// apps/worker/src/ai/sast/capella/temporal/activity-types.ts.
const OPERATION_ACTIVITY_PROGRESS: Readonly<Record<string, ActivityProgressSpec>> = {
runPreflightValidation: { key: 'preflight', label: 'Preflight validation', kind: 'operation' },
runPreflightValidation: { key: 'preflight', label: 'Preflight', kind: 'operation' },
runCyberAccessVerification: { key: 'cyber-access', label: 'Cyber access verification', kind: 'operation' },
syncPlaywrightStealthConfig: { key: 'preflight', label: 'Browser setup', kind: 'operation' },
initDeliverableGit: { key: 'scan-initialization', label: 'Initialize deliverables', kind: 'operation' },
syncCodePathDenyRules: { key: 'scan-initialization', label: 'Apply source rules', kind: 'operation' },
@@ -352,6 +355,8 @@ export type PipelineStatus = 'running' | 'completed' | 'failed' | 'cancelled' |
export interface PipelineState {
readonly status: PipelineStatus;
readonly authOnly?: boolean;
readonly validateModel?: boolean;
readonly currentPhase: string | null;
readonly currentAgent: string | null;
readonly completedAgents: string[];
+3 -1
View File
@@ -75,6 +75,8 @@ function isProviderFailureCategory(value: unknown): value is string {
}
const OPERATION_LABELS = new Set([
'Preflight',
'Cyber access verification',
'Agentic SAST',
// Capella stage rows, signalled up from the SAST child workflow. Mirrors
// CAPELLA_STAGE_LABELS in apps/worker/src/ai/sast/types.ts, minus the deterministic
@@ -228,7 +230,7 @@ export function safeOperationLabel(value: string): string {
export function safeOperationKey(value: string): string {
if (
/^(?:agentic-sast|miscellaneous-pipeline|report:(?:initialize|assemble|compact|checkpoint|finalize|finalize-degraded|terminal|surface))$/u.test(
/^(?:preflight|cyber-access|agentic-sast|miscellaneous-pipeline|report:(?:initialize|assemble|compact|checkpoint|finalize|finalize-degraded|terminal|surface))$/u.test(
value,
) ||
/^agentic-sast:(?:architecture|threat-model|plan|research|dedupe|review|critic|confirm|calibrate)$/u.test(value) ||