mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-08-11 14:00:24 +02:00
feat: multi-provider model support, SARIF output, and exploit-mode fixes (#402)
* feat(worker): record token, cache, and turn usage per agent * feat: replace model tiers with a single SHANNON_AI_MODEL across five providers * feat(cli): rebuild the setup wizard for provider and model selection * docs: document single-model selection and supported providers * feat(worker): use chat completions for OpenAI behind a custom base URL * feat: add SHANNON_AI_OPENAI_FORMAT to pick the wire API for OpenAI gateways * refactor(cli): drop endpoint path hints from the gateway format picker * feat(worker): enable pi in-session provider retry with retry-after backoff * refactor(worker): hand provider error classification to pi and drop the Anthropic ladders * refactor: remove the subscription retry preset and pipeline config section * fix(worker): validate Bedrock credentials with the same live probe as other providers * feat(worker): render the report from structured findings instead of agent-written markdown * fix(worker): dispose the credential probe session on every path * fix(worker): refuse to replace the assembled report with an empty one * refactor(worker): catch post-processing throws across the whole finalization block * revert(worker): drop the report zero-findings guard * docs(worker): correct the retry split and Bedrock credential claims * docs: regenerate llms-full.txt from current sources * feat(cli): build and run the npx flow from a clone * refactor(cli): flatten the setup summary output * feat(cli): reject runs with more than one provider configured * fix(worker): say a rejected bash call never ran * chore(cli): drop grok-4.3 and gpt-5.6-luna from the setup suggestions * feat(worker): capture structured finding locations for SARIF output * fix(worker): enumerate queue confidence so the report inherits it verbatim * feat(worker): give the reporting phase a mode-specific output schema * feat(worker): emit a SARIF 2.1.0 log for exploitative runs * fix(worker): correct SARIF locations and defer fingerprinting to the upload action * fix(worker): drop the confidence suffix from the analysis-mode summary list * feat(worker): give exploit findings a dedicated code location field * feat(worker): carry structured code locations from the vuln queue to the report * fix(worker): join code locations from the vuln queue instead of re-asking agents * fix(worker): spell out the finding_id to category mapping in the tool schema * feat: drop Google/Gemini as a supported AI provider * fix(worker): stop asking the report agent for code locations * docs: correct the provider list and drop the removed rate-limit settings * docs: add provider cyber safeguards and suggested models per provider * docs: document the SARIF output and the report rating thresholds
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2025 Keygraph, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License version 3
|
||||
// as published by the Free Software Foundation.
|
||||
|
||||
/**
|
||||
* Attach vuln-queue code locations to collected findings.
|
||||
*
|
||||
* The vuln agent authors `code_locations` once, into its queue. Every stage after that used to
|
||||
* re-transcribe them — the exploit agent into its evidence, the report agent into `add_finding` —
|
||||
* and each hop lost some: 100% in the queue, 98% in the evidence, 42-63% by the report. Nothing
|
||||
* about the copy is a judgement call, and `finding_id` matches the queue `ID` exactly, so the
|
||||
* locations are joined here instead of being asked for again.
|
||||
*/
|
||||
|
||||
import { fs, path } from 'zx';
|
||||
import type { QueueCodeLocation } from '../ai/queue-schemas.js';
|
||||
import type { AddFindingInput } from '../collectors/finding-collector.js';
|
||||
import type { ActivityLogger } from '../types/activity-logger.js';
|
||||
import { ALL_VULN_CLASSES } from '../types/config.js';
|
||||
|
||||
interface QueueEntry {
|
||||
ID?: string;
|
||||
code_locations?: QueueCodeLocation[];
|
||||
}
|
||||
|
||||
/** Read every per-class queue in the deliverables dir into an ID-to-locations map. */
|
||||
async function loadQueueLocations(
|
||||
deliverablesPath: string,
|
||||
logger: ActivityLogger,
|
||||
): Promise<Map<string, QueueCodeLocation[]>> {
|
||||
const locations = new Map<string, QueueCodeLocation[]>();
|
||||
|
||||
for (const vulnClass of ALL_VULN_CLASSES) {
|
||||
const queuePath = path.join(deliverablesPath, `${vulnClass}_exploitation_queue.json`);
|
||||
if (!(await fs.pathExists(queuePath))) continue;
|
||||
|
||||
try {
|
||||
const doc = (await fs.readJson(queuePath)) as { vulnerabilities?: QueueEntry[] };
|
||||
for (const entry of doc.vulnerabilities ?? []) {
|
||||
if (entry.ID && entry.code_locations && entry.code_locations.length > 0) {
|
||||
locations.set(entry.ID, entry.code_locations);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(`Could not read ${vulnClass} queue for code locations: ${(error as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the findings with `code_locations` filled in from the queue.
|
||||
*
|
||||
* A finding with no matching queue entry keeps none — the join never invents one. Findings are
|
||||
* copied rather than mutated so the collector's own state stays untouched.
|
||||
*/
|
||||
export async function attachQueueCodeLocations(
|
||||
findings: readonly AddFindingInput[],
|
||||
deliverablesPath: string,
|
||||
logger: ActivityLogger,
|
||||
): Promise<AddFindingInput[]> {
|
||||
const byId = await loadQueueLocations(deliverablesPath, logger);
|
||||
if (byId.size === 0) return [...findings];
|
||||
|
||||
let matched = 0;
|
||||
const joined = findings.map((finding) => {
|
||||
const locations = byId.get(finding.finding_id);
|
||||
if (!locations) return finding;
|
||||
matched += 1;
|
||||
return { ...finding, code_locations: locations };
|
||||
});
|
||||
|
||||
logger.info(`Attached code locations to ${matched}/${findings.length} finding(s) from the vuln queues`);
|
||||
return joined;
|
||||
}
|
||||
Reference in New Issue
Block a user