mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-10-19 22:27:19 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e5b8e88db |
No files matched your search
@@ -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> <a href="https://keygraph.io/?utm_source=github&utm_medium=referral&utm_campaign=shannon_oss_2026q4&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> <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
@@ -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> <a href="https://keygraph.io/?utm_source=npm&utm_medium=referral&utm_campaign=shannon_oss_2026q4&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> <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&utm_medium=referral&utm_campaign=shannon_oss_2026q4&utm_content=readme_footer">Keygraph</a></b>
|
||||
<b>Built by <a href="https://keygraph.io">Keygraph</a></b>
|
||||
</p>
|
||||
@@ -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();
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user