Compare commits

..
5 changed files with 40 additions and 33 deletions

No files matched your search

+7 -7
View File
@@ -27,7 +27,7 @@ npx @keygraph/shannon@latest
---
<a href="https://discord.gg/9ZqQPuhJB7"><picture><source media="(prefers-color-scheme: dark)" srcset="./assets/discord_button_dark.png"><source media="(prefers-color-scheme: light)" srcset="./assets/discord_button_light.png"><img src="./assets/discord_button_light.png" height="40" alt="Join Discord"></picture></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://keygraph.io/?utm_source=github&amp;utm_medium=referral&amp;utm_campaign=shannon_oss_2026q4&amp;utm_content=readme_header_button"><picture><source media="(prefers-color-scheme: dark)" srcset="./assets/keygraph_button_dark.png"><source media="(prefers-color-scheme: light)" srcset="./assets/keygraph_button_light.png"><img src="./assets/keygraph_button_light.png" height="40" alt="Visit Keygraph.io"></picture></a>
<a href="https://discord.gg/9ZqQPuhJB7"><picture><source media="(prefers-color-scheme: dark)" srcset="./assets/discord_button_dark.png"><source media="(prefers-color-scheme: light)" srcset="./assets/discord_button_light.png"><img src="./assets/discord_button_light.png" height="40" alt="Join Discord"></picture></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://keygraph.io/"><picture><source media="(prefers-color-scheme: dark)" srcset="./assets/keygraph_button_dark.png"><source media="(prefers-color-scheme: light)" srcset="./assets/keygraph_button_light.png"><img src="./assets/keygraph_button_light.png" height="40" alt="Visit Keygraph.io"></picture></a>
---
@@ -72,11 +72,11 @@ npx @keygraph/shannon@latest
## What is Shannon?
Shannon is an autonomous AI pentester developed by [Keygraph](https://keygraph.io/?utm_source=github&utm_medium=referral&utm_campaign=shannon_oss_2026q4&utm_content=readme_intro). It performs security testing of web applications and their underlying APIs by combining source-code analysis with live exploitation.
Shannon is an autonomous AI pentester developed by [Keygraph](https://keygraph.io). It performs security testing of web applications and their underlying APIs by combining source-code analysis with live exploitation.
Shannon analyzes your web application's source code to identify potential attack vectors, then uses browser automation and command-line tools to execute real exploits against the running application and its APIs. Only vulnerabilities with a working proof-of-concept are included in the final report.
Shannon is the agent. This repository is Shannon, the open-source pentester you run yourself. The same Shannon also powers Keygraph's commercial editions, [Pro and Enterprise](https://keygraph.io/pricing?utm_source=github&utm_medium=referral&utm_campaign=shannon_oss_2026q4&utm_content=readme_intro_pricing). See [Editions](#editions) for how Shannon compares with Pro, Enterprise, and the Community Program.
Shannon is the agent. This repository is Shannon, the open-source pentester you run yourself. The same Shannon also powers Keygraph's commercial editions, [Pro and Enterprise](https://keygraph.io/pricing). See [Editions](#editions) for how Shannon compares with Pro, Enterprise, and the Community Program.
<a id="why-shannon-exists"></a>
<details>
@@ -242,7 +242,7 @@ See the [Shannon GitHub Action documentation](https://github.com/KeygraphHQ/shan
| **Pro** | Keygraph's cloud-hosted edition, managed by Keygraph, with every module included. | $50 per active developer per month. |
| **Enterprise** | Everything in Pro, self-hosted in your own environment or fully air-gapped. | Custom. |
See [keygraph.io/pricing](https://keygraph.io/pricing?utm_source=github&utm_medium=referral&utm_campaign=shannon_oss_2026q4&utm_content=readme_editions_pricing) for current prices and the full feature table.
See [keygraph.io/pricing](https://keygraph.io/pricing) for current prices and the full feature table.
**Pro**, **Enterprise**, and the **Community Program** are powered by an enhanced, enterprise-grade version of the Shannon engine, and all three add:
@@ -252,7 +252,7 @@ See [keygraph.io/pricing](https://keygraph.io/pricing?utm_source=github&utm_medi
- **Fix pull requests and retests**: reviewable fix pull requests, with each fix verified by re-analysis and exploit replay, without a full rescan.
- **Jira and access control**: two-way Jira sync, SSO, RBAC, and audit logs.
No source code? Use the [Blackbox Pentester](https://keygraph.io/agentic-blackbox-pentester?utm_source=github&utm_medium=referral&utm_campaign=shannon_oss_2026q4&utm_content=readme_editions_blackbox).
No source code? Use the [Blackbox Pentester](https://keygraph.io/agentic-blackbox-pentester).
[Compare Shannon, Pro, and Enterprise in detail →](docs/keygraph-platform.md)
@@ -364,7 +364,7 @@ At this time, Keygraph is not accepting external code contributions. Issues are
Stay connected:
- [Keygraph website](https://keygraph.io/?utm_source=github&utm_medium=referral&utm_campaign=shannon_oss_2026q4&utm_content=readme_community_website)
- [Keygraph website](https://keygraph.io)
- [Twitter/X: @KeygraphHQ](https://twitter.com/KeygraphHQ)
- [LinkedIn: Keygraph](https://linkedin.com/company/keygraph)
@@ -400,4 +400,4 @@ Shannon works with local models served through Ollama, vLLM, or LM Studio, which
Shannon executes real exploits. It reports a finding only when it has produced a working proof-of-concept, and discards hypotheses it cannot prove. It is a pentester, not a passive scanner.
**Built by [Keygraph](https://keygraph.io/?utm_source=github&utm_medium=referral&utm_campaign=shannon_oss_2026q4&utm_content=readme_footer)**
**Built by [Keygraph](https://keygraph.io)**
+3 -3
View File
@@ -10,7 +10,7 @@ It analyzes your source code, identifies attack paths, and executes real exploit
---
<a href="https://discord.gg/9ZqQPuhJB7"><img src="https://raw.githubusercontent.com/KeygraphHQ/shannon/main/assets/discord_button_light.png" height="40" alt="Join Discord"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://keygraph.io/?utm_source=npm&amp;utm_medium=referral&amp;utm_campaign=shannon_oss_2026q4&amp;utm_content=readme_header_button"><img src="https://raw.githubusercontent.com/KeygraphHQ/shannon/main/assets/keygraph_button_light.png" height="40" alt="Visit Keygraph.io"></a>
<a href="https://discord.gg/9ZqQPuhJB7"><img src="https://raw.githubusercontent.com/KeygraphHQ/shannon/main/assets/discord_button_light.png" height="40" alt="Join Discord"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://keygraph.io/"><img src="https://raw.githubusercontent.com/KeygraphHQ/shannon/main/assets/keygraph_button_light.png" height="40" alt="Visit Keygraph.io"></a>
---
@@ -41,7 +41,7 @@ Shannon pulls the worker image from Docker Hub, starts the required local infras
## Editions
**Shannon** is this package: the open-source pentester you run yourself, on demand, and complete in that lane. **Keygraph Pro** and **Keygraph Enterprise** are the commercial editions. They run an enhanced build of Shannon continuously and close the full AppSec lifecycle around it: code analysis, finding management, automated remediation, verification, and enterprise deployment. The **Community Program** offers Pro at no cost to organizations that qualify. See [keygraph.io/pricing](https://keygraph.io/pricing?utm_source=npm&utm_medium=referral&utm_campaign=shannon_oss_2026q4&utm_content=readme_editions_pricing).
**Shannon** is this package: the open-source pentester you run yourself, on demand, and complete in that lane. **Keygraph Pro** and **Keygraph Enterprise** are the commercial editions. They run an enhanced build of Shannon continuously and close the full AppSec lifecycle around it: code analysis, finding management, automated remediation, verification, and enterprise deployment. The **Community Program** offers Pro at no cost to organizations that qualify. See [keygraph.io/pricing](https://keygraph.io/pricing).
## Documentation
@@ -56,5 +56,5 @@ Commercial and enterprise licensing is available for organizations that need dif
For commercial licensing, contact [shannon@keygraph.io](mailto:shannon@keygraph.io).
<p align="center">
<b>Built by <a href="https://keygraph.io/?utm_source=npm&amp;utm_medium=referral&amp;utm_campaign=shannon_oss_2026q4&amp;utm_content=readme_footer">Keygraph</a></b>
<b>Built by <a href="https://keygraph.io">Keygraph</a></b>
</p>
+28 -21
View File
@@ -184,6 +184,9 @@ async function waitForWorkflowClosure(
/**
* Request cooperative cancellation, then make at most two termination attempts when the
* workflow does not close during its grace period. Every success is backed by direct state.
*
* A grace of zero skips the cooperative phase and terminates directly: a workflow with no live
* worker can never honour a cooperative cancel, so callers pass `graceMs = 0` for those targets.
*/
export async function stopWorkflowCancelFirst(
workflowId: string,
@@ -192,14 +195,16 @@ export async function stopWorkflowCancelFirst(
pollMs: number = CANCELLATION_POLL_MS,
verifyMs: number = TERMINATION_VERIFY_MS,
): Promise<WorkflowStopOutcome> {
try {
if ((await lifecycle.cancel(workflowId)) === 'not-found') return { kind: 'already-closed' };
} catch {
// The request may have reached Temporal even when its acknowledgement was lost.
}
if (graceMs > 0) {
try {
if ((await lifecycle.cancel(workflowId)) === 'not-found') return { kind: 'already-closed' };
} catch {
// The request may have reached Temporal even when its acknowledgement was lost.
}
if (await waitForWorkflowClosure(workflowId, lifecycle, lifecycle.now() + graceMs, pollMs)) {
return { kind: 'graceful' };
if (await waitForWorkflowClosure(workflowId, lifecycle, lifecycle.now() + graceMs, pollMs)) {
return { kind: 'graceful' };
}
}
const verifyPerAttemptMs = Math.max(pollMs, Math.ceil(verifyMs / TERMINATION_ATTEMPTS));
@@ -225,16 +230,21 @@ export async function stopWorkflowCancelFirst(
return { kind: 'unverified' };
}
/** Apply the same bounded lifecycle concurrently to a captured set of workflow IDs. */
/**
* Apply the bounded lifecycle concurrently to a captured set of targets, preserving input order in
* the returned outcomes. A target with no live worker (`containerCandidate === false`) gets no grace.
*/
export async function stopWorkflowsCancelFirst(
workflowIds: readonly string[],
targets: readonly WorkflowStopTarget[],
lifecycle: StopLifecycle = stopLifecycle,
graceMs: number = CANCELLATION_GRACE_MS,
pollMs: number = CANCELLATION_POLL_MS,
verifyMs: number = TERMINATION_VERIFY_MS,
): Promise<readonly WorkflowStopOutcome[]> {
const settlements = await Promise.allSettled(
workflowIds.map((workflowId) => stopWorkflowCancelFirst(workflowId, lifecycle, graceMs, pollMs, verifyMs)),
targets.map((target) =>
stopWorkflowCancelFirst(target.workflowId, lifecycle, target.containerCandidate ? graceMs : 0, pollMs, verifyMs),
),
);
return settlements.map((settlement) =>
settlement.status === 'fulfilled' ? settlement.value : { kind: 'unverified' },
@@ -378,13 +388,7 @@ export async function executeStopPlan(
verifyMs: number = TERMINATION_VERIFY_MS,
candidateSettleMs: number = CANDIDATE_REGISTRATION_SETTLE_MS,
): Promise<StopExecutionResult> {
const initialOutcomes = await stopWorkflowsCancelFirst(
targets.map((target) => target.workflowId),
lifecycle,
graceMs,
pollMs,
verifyMs,
);
const initialOutcomes = await stopWorkflowsCancelFirst(targets, lifecycle, graceMs, pollMs, verifyMs);
const outcomes = new Map<string, WorkflowStopOutcome>();
for (let index = 0; index < targets.length; index++) {
const target = targets[index];
@@ -717,10 +721,7 @@ export async function stopVisibleWorkflowsUntilSettled(
recheckedAlreadyClosed.add(target.workflowId);
}
}
const outcomes = await stopWorkflowsCancelFirst(
residualTargets.map((target) => target.workflowId),
lifecycle,
);
const outcomes = await stopWorkflowsCancelFirst(residualTargets, lifecycle);
for (let index = 0; index < residualTargets.length; index++) {
const target = residualTargets[index];
const outcome = outcomes[index];
@@ -759,6 +760,12 @@ async function stopAllScans(yes: boolean): Promise<void> {
if (initialDiscovery.kind === 'unavailable') {
fail('Could not verify whether scan workflows are running in Temporal.', `Retry: ${commandPrefix()} stop --all`);
}
// The settle loop below only catches a scan whose workflow is mid-launch and not yet visible,
// and such a launch always leaves a pending record. With none, nothing can appear — skip it.
if (pending.references.length === 0) {
console.log('No running scans to stop.');
return;
}
const emptySettleDeadline = stopLifecycle.now() + VISIBILITY_MAX_SETTLE_MS;
while (targets.length === 0) {
const remaining = emptySettleDeadline - stopLifecycle.now();
+1 -1
View File
@@ -583,7 +583,7 @@ export function runningScanContainersChecked(
filter: readonly string[] = WORKER_FILTER,
): CommandQueryResult<RunningScanContainer[]> {
try {
const format = `{{.ID}}\t{{ index .Labels "${WORKSPACE_LABEL}" }}\t{{ index .Labels "${TASK_QUEUE_LABEL}" }}\t{{ index .Labels "${WORKFLOW_ID_LABEL}" }}\t{{ index .Labels "${WORKER_PROTOCOL_LABEL}" }}`;
const format = `{{.ID}}\t{{.Label "${WORKSPACE_LABEL}"}}\t{{.Label "${TASK_QUEUE_LABEL}"}}\t{{.Label "${WORKFLOW_ID_LABEL}"}}\t{{.Label "${WORKER_PROTOCOL_LABEL}"}}`;
const output = execFileSync('docker', ['ps', ...filter, '--format', format], {
stdio: 'pipe',
encoding: 'utf-8',
+1 -1
View File
@@ -17,7 +17,7 @@
#let tester-override = sys.inputs.at("tester", default: "Shannon")
#let brand = sys.inputs.at("brand", default: "Shannon | AI Pentester by Keygraph")
#let keygraph-url = "https://keygraph.io/?utm_source=shannon&utm_medium=product&utm_campaign=shannon_oss_2026q4&utm_content=pdf_report"
#let keygraph-url = "https://keygraph.io"
// ---------- Palette ---------------------------------------------------------
// Kept distinct so Critical / High are not confused under monitor gamma.