@stroq/cli 0.19.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -16
- package/dist/index.js +30 -30
- package/openclaw-plugin/openclaw.plugin.json +1 -1
- package/openclaw-plugin/package.json +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -9,7 +9,18 @@
|
|
|
9
9
|
<a href="https://github.com/AGGIB/Stroq/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="License: Apache 2.0"></a>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
# Stroq — find known secrets in AI coding-agent sessions
|
|
13
|
+
|
|
14
|
+
**Check a session you already ran:** `stroq sent` looks for exact matches to credentials currently available on your machine in local Claude Code, Codex CLI and Cursor session records. It reports the credential name, source file and recorded tool call without printing the value. A match proves that the value appears in a local record; it does not prove that a model provider received it, that the credential is still valid or that a breach occurred.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @stroq/cli sent --last # inspect the newest supported session in this project
|
|
18
|
+
npx @stroq/cli replay --last # trace recorded content to later agent actions
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Run these commands from the project directory with Node.js 22 or newer. They work on sessions from before Stroq was installed. `npx` downloads the CLI from npm; `sent` reads local credential sources to know what values to match and does not upload them. See the [guide's coverage and limits](https://github.com/AGGIB/Stroq/blob/main/docs/GUIDE.md).
|
|
22
|
+
|
|
23
|
+
**Guard future sessions:** Stroq scans content visible to installed agent hooks, carries suspicion and source information into later decisions, and applies a local policy to the tool calls those hooks expose. It supports native hooks, an OpenClaw plugin and a stdio MCP proxy; coverage differs by agent and channel. The hooks do not send telemetry.
|
|
13
24
|
|
|
14
25
|
Supported today: **Claude Code**, **Cursor**, **Codex**, **Copilot CLI**, **Windsurf**, **Google Antigravity** (native hooks) · **OpenClaw** (in-process plugin) · **any MCP client** (stdio proxy).
|
|
15
26
|
|
|
@@ -22,6 +33,7 @@ npx @stroq/cli init --agent codex # Codex CLI: writes .codex/hooks.json
|
|
|
22
33
|
npx @stroq/cli init --agent copilot # Copilot CLI: writes .github/hooks/stroq.json
|
|
23
34
|
npx @stroq/cli init --agent openclaw # OpenClaw: installs a plugin into ~/.stroq/openclaw-plugin
|
|
24
35
|
npx @stroq/cli init --agent windsurf # Windsurf: merges into .windsurf/hooks.json
|
|
36
|
+
npx @stroq/cli init --agent antigravity # Google Antigravity: merges into .agents/hooks.json
|
|
25
37
|
npx @stroq/cli init --agent mcp --client claude-desktop # any MCP client: wraps its stdio servers
|
|
26
38
|
npx @stroq/cli doctor # check the installation
|
|
27
39
|
```
|
|
@@ -36,21 +48,23 @@ MCP proxy note: for clients with no hook API, `--agent mcp` rewrites the client'
|
|
|
36
48
|
|
|
37
49
|
## Commands
|
|
38
50
|
|
|
39
|
-
| Command | What it does
|
|
40
|
-
| ------------------------------------------------------- |
|
|
41
|
-
| `stroq init [--agent <name>] [--user] [--dry-run]` | Install hooks for `claude-code`, `cursor`, `codex`, `copilot`, `openclaw` or `
|
|
42
|
-
| `stroq hook <agent>` | Hook entrypoint (reads the event on stdin; `copilot` and `openclaw` take a `pre`/`post` argument, the others do not)
|
|
43
|
-
| `stroq mcp --server <name> -- <cmd>` | MCP stdio proxy: judges every `tools/call` and scans every result for one wrapped server
|
|
44
|
-
| `stroq doctor` | Check Node version, rules, hooks for every agent, self-test
|
|
45
|
-
| `stroq log [--count 20]`
|
|
46
|
-
| `stroq verify` | Verify the audit hash chain
|
|
47
|
-
| `stroq untaint [--session <id>] [--all]` | Clear a false-positive session's taint and provenance, or every session's
|
|
48
|
-
| `stroq why [--seq <n>]` | Explain the most recent denied/asked action: rule, provenance, taint
|
|
49
|
-
| `stroq
|
|
50
|
-
| `stroq
|
|
51
|
-
| `stroq
|
|
52
|
-
| `stroq
|
|
53
|
-
| `stroq
|
|
51
|
+
| Command | What it does |
|
|
52
|
+
| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| `stroq init [--agent <name>] [--user] [--dry-run]` | Install hooks for `claude-code`, `cursor`, `codex`, `copilot`, `openclaw`, `windsurf` or `antigravity`, or wrap an MCP client's stdio servers with `--agent mcp --client <name>` or `--config <path>` (`--user` for the home-directory copy) |
|
|
54
|
+
| `stroq hook <agent>` | Hook entrypoint (reads the event on stdin; `copilot` and `openclaw` take a `pre`/`post` argument, the others do not) |
|
|
55
|
+
| `stroq mcp --server <name> -- <cmd>` | MCP stdio proxy: judges every `tools/call` and scans every result for one wrapped server |
|
|
56
|
+
| `stroq doctor` | Check Node version, rules, hooks for every agent, self-test |
|
|
57
|
+
| `stroq log [--count 20] [--json]` | Show recent audit entries; `--json` prints one entry per line |
|
|
58
|
+
| `stroq verify` | Verify the audit hash chain |
|
|
59
|
+
| `stroq untaint [--session <id>] [--all]` | Clear a false-positive session's taint and provenance, or every session's |
|
|
60
|
+
| `stroq why [--seq <n>]` | Explain the most recent denied/asked action: rule, provenance, taint |
|
|
61
|
+
| `stroq sent [--last] [--transcript <path>] [--json]` | Match known local credentials against a recorded session, including tool results where a transcript is available; report names and sources, never values |
|
|
62
|
+
| `stroq replay [--last] [--transcript <path>] [--json]` | Reconstruct which recorded content preceded later agent actions and show the matching evidence |
|
|
63
|
+
| `stroq canary [--name <NAME>] [--file <path>]` | Print a canary secret to plant; its outbound use is denied and taints the session. `--file` plants it as a decoy file: any call naming the file is denied and taints the session |
|
|
64
|
+
| `stroq attack [--json] [--only <id>] [--fuzz]` | Replay 21 documented-incident and synthetic scenarios against your policy; `--fuzz` crosses each with a deterministic mutation set and reports variants that reach `allow`; exit 1 if any gets through |
|
|
65
|
+
| `stroq exposure [--probe] [--share] [--json]` | Map this machine's agent surface and report what reaches you; exit 1 on any finding |
|
|
66
|
+
| `stroq bench [--corpus <dir>] [--json] [--verbose]` | Measure the shipped rule set's false-positive rate against a corpus of benign developer documentation; `--verbose` lists the flagged files |
|
|
67
|
+
| `stroq coverage [--format <table\|navigator>] [--json]` | Print the control mapping against MITRE ATLAS and OWASP ASI, built from the attack corpus's own scenario tags; `--format=navigator` emits an ATT&CK Navigator layer |
|
|
54
68
|
|
|
55
69
|
`stroq exposure` reads files only. `--probe` is the one flag that starts a process: it launches each configured stdio MCP server, asks once for `tools/list`, scans the tool descriptions and shuts the server down — no tool is ever called. Without it, tool-description poisoning is not covered by the run, and the report says so. `--share` prints a redacted summary built from a whitelist, locally; nothing is transmitted.
|
|
56
70
|
|
package/dist/index.js
CHANGED
|
@@ -11711,8 +11711,8 @@ var VIA_LABEL = {
|
|
|
11711
11711
|
tool_argument: "in the arguments of"
|
|
11712
11712
|
};
|
|
11713
11713
|
var FILE_EVIDENCE_LINE = {
|
|
11714
|
-
read: "its
|
|
11715
|
-
named: "named in
|
|
11714
|
+
read: "a Read/Grep call was recorded for this path; its returned content is not established here",
|
|
11715
|
+
named: "named in a shell command; whether it read or printed the file is unknown"
|
|
11716
11716
|
};
|
|
11717
11717
|
function timestamp(iso) {
|
|
11718
11718
|
return Number.isNaN(Date.parse(iso)) ? "an unrecorded time" : iso.replace(".000Z", "Z");
|
|
@@ -11751,8 +11751,8 @@ function coverageLines(report2) {
|
|
|
11751
11751
|
const lines = [
|
|
11752
11752
|
"COVERAGE",
|
|
11753
11753
|
` Matched against ${c.indexedSecrets} value(s) indexed from: ${sources}`,
|
|
11754
|
-
"
|
|
11755
|
-
" and
|
|
11754
|
+
" To match known values, this command reads supported local credential files",
|
|
11755
|
+
" and project .env sources. It stores and prints names and sources, never values.",
|
|
11756
11756
|
" Credential-shaped variables in the environment this command ran with are matched",
|
|
11757
11757
|
" too, and are not counted above.",
|
|
11758
11758
|
" A credential you have rotated or deleted since that session is not in the index,",
|
|
@@ -11760,39 +11760,38 @@ function coverageLines(report2) {
|
|
|
11760
11760
|
];
|
|
11761
11761
|
if (c.toolResultsRead) {
|
|
11762
11762
|
lines.push(
|
|
11763
|
-
`
|
|
11763
|
+
` Recorded tool result text was scanned from the agent transcript (${c.results} of ${c.calls} calls).`
|
|
11764
11764
|
);
|
|
11765
11765
|
} else {
|
|
11766
11766
|
lines.push(
|
|
11767
|
-
" The audit log records
|
|
11768
|
-
"
|
|
11769
|
-
" to read the agent's own transcript instead,
|
|
11767
|
+
" The audit log records call arguments, never what came back, so only recorded",
|
|
11768
|
+
" argument matches and supported file-path references are covered here.",
|
|
11769
|
+
" Run `stroq sent --last` to read the agent's own transcript instead,",
|
|
11770
|
+
" which may still have the result text."
|
|
11770
11771
|
);
|
|
11771
11772
|
}
|
|
11772
11773
|
return lines;
|
|
11773
11774
|
}
|
|
11774
11775
|
var MEANING = [
|
|
11775
11776
|
"WHAT THIS SAYS, AND WHAT IT DOES NOT",
|
|
11776
|
-
"
|
|
11777
|
-
"
|
|
11778
|
-
"
|
|
11779
|
-
"
|
|
11780
|
-
"
|
|
11781
|
-
"
|
|
11777
|
+
" These findings come from a local agent transcript or Stroq audit log.",
|
|
11778
|
+
" Where shown, a matching value appeared in a tool result or call argument.",
|
|
11779
|
+
" A file entry records a supported tool using or naming its path.",
|
|
11780
|
+
" This record alone cannot confirm a later model request, delivery to a provider,",
|
|
11781
|
+
" the recipient, retention, human access, or whether a credential is still valid.",
|
|
11782
|
+
" It does not prove a breach. Review the session and rotate active credentials",
|
|
11783
|
+
" when the potential exposure warrants it."
|
|
11782
11784
|
];
|
|
11783
11785
|
function formatSent(report2) {
|
|
11784
11786
|
const lines = [
|
|
11785
|
-
"stroq sent \u2014
|
|
11787
|
+
"stroq sent \u2014 credential evidence in recorded agent sessions",
|
|
11786
11788
|
"",
|
|
11787
11789
|
` session ${report2.sessionId} \xB7 ${report2.coverage.calls} tool call(s)${duration2(report2)}`,
|
|
11788
11790
|
origin(report2),
|
|
11789
11791
|
""
|
|
11790
11792
|
];
|
|
11791
11793
|
if (report2.credentials.length > 0) {
|
|
11792
|
-
lines.push(
|
|
11793
|
-
`CREDENTIALS THAT WERE IN THIS SESSION'S TRAFFIC (${report2.credentials.length})`,
|
|
11794
|
-
""
|
|
11795
|
-
);
|
|
11794
|
+
lines.push(`CREDENTIAL VALUES FOUND IN THIS SESSION RECORD (${report2.credentials.length})`, "");
|
|
11796
11795
|
for (const credential of report2.credentials) lines.push(...credentialBlock(credential));
|
|
11797
11796
|
}
|
|
11798
11797
|
if (report2.files.length > 0) {
|
|
@@ -11804,16 +11803,16 @@ function formatSent(report2) {
|
|
|
11804
11803
|
if (report2.files.some((f) => f.evidence === "read")) {
|
|
11805
11804
|
lines.push(
|
|
11806
11805
|
"",
|
|
11807
|
-
" A
|
|
11808
|
-
"
|
|
11806
|
+
" A Read/Grep result may contain sensitive content absent from today's index.",
|
|
11807
|
+
" Inspect the agent transcript to learn what was actually returned."
|
|
11809
11808
|
);
|
|
11810
11809
|
}
|
|
11811
11810
|
lines.push("");
|
|
11812
11811
|
}
|
|
11813
11812
|
if (report2.credentials.length === 0 && report2.files.length === 0) {
|
|
11814
11813
|
lines.push(
|
|
11815
|
-
"No indexed credential
|
|
11816
|
-
"
|
|
11814
|
+
"No indexed credential match or credential-file finding was recorded for",
|
|
11815
|
+
"this session. Read the coverage below before treating that as clean.",
|
|
11817
11816
|
""
|
|
11818
11817
|
);
|
|
11819
11818
|
} else {
|
|
@@ -12758,7 +12757,7 @@ function emit(report2, out) {
|
|
|
12758
12757
|
const found = report2.credentials.length > 0 || report2.files.length > 0;
|
|
12759
12758
|
return out.failOnFinding && found ? 1 : 0;
|
|
12760
12759
|
}
|
|
12761
|
-
var USAGE = `stroq sent \u2014
|
|
12760
|
+
var USAGE = `stroq sent \u2014 credential evidence in recorded agent sessions
|
|
12762
12761
|
|
|
12763
12762
|
stroq sent --last read the newest session in this directory
|
|
12764
12763
|
stroq sent --transcript <path> read a specific transcript or rollout
|
|
@@ -12774,7 +12773,7 @@ Reads sessions recorded by ${readerLabels()}.
|
|
|
12774
12773
|
addressed as <store>#<session>. It reads this machine's credential files to
|
|
12775
12774
|
know what to look for, and prints names and sources only, never a value. A
|
|
12776
12775
|
finding exits 0 by default: a session that already happened cannot be
|
|
12777
|
-
|
|
12776
|
+
changed by today's commit. A transcript match does not confirm provider delivery.
|
|
12778
12777
|
`;
|
|
12779
12778
|
async function runSent(args) {
|
|
12780
12779
|
let parsed;
|
|
@@ -13073,15 +13072,16 @@ Commands:
|
|
|
13073
13072
|
untaint [--session <id>] [--all] clear a false-positive session's taint, or every session's
|
|
13074
13073
|
why [--seq <n>] explain the most recent denied/asked action: rule, provenance, taint
|
|
13075
13074
|
replay [<session>] [--last] [--transcript <path>] [--json] [--list]
|
|
13076
|
-
rebuild
|
|
13077
|
-
and which actions
|
|
13075
|
+
rebuild the recorded sequence: which content the agent read,
|
|
13076
|
+
and which later actions matched it. --last reads the agent's own
|
|
13078
13077
|
transcript, so it works on sessions that ran before you installed
|
|
13079
13078
|
sent [<session>] [--last] [--transcript <path>] [--json] [--fail-on-finding]
|
|
13080
|
-
which
|
|
13081
|
-
which
|
|
13079
|
+
which credentials appear in a recorded agent session, and in
|
|
13080
|
+
which tool result or call. This local evidence does not confirm
|
|
13081
|
+
a model request or provider delivery. --last reads
|
|
13082
13082
|
the agent's own transcript, so it covers sessions from before
|
|
13083
13083
|
you installed and can see what tools RETURNED, not just what
|
|
13084
|
-
they
|
|
13084
|
+
they called. To match values it reads this machine's credential
|
|
13085
13085
|
files (~/.aws/credentials, ~/.npmrc, ~/.netrc,
|
|
13086
13086
|
~/.docker/config.json, ./.env*); it reports names and sources
|
|
13087
13087
|
only, never a value. Exits 0 even when it finds something \u2014
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "stroq",
|
|
3
3
|
"name": "Stroq",
|
|
4
4
|
"description": "Local action firewall for OpenClaw: scans what the agent reads, taints the session, blocks or asks before dangerous tool calls.",
|
|
5
|
-
"version": "0.19.
|
|
5
|
+
"version": "0.19.1",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stroq/cli",
|
|
3
|
-
"version": "0.19.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.19.1",
|
|
4
|
+
"description": "Find known secrets in local Claude Code, Codex and Cursor sessions; trace risky AI agent actions and guard future tool calls",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -32,10 +32,13 @@
|
|
|
32
32
|
"agent-security",
|
|
33
33
|
"prompt-injection",
|
|
34
34
|
"claude-code",
|
|
35
|
+
"codex",
|
|
36
|
+
"openclaw",
|
|
35
37
|
"mcp",
|
|
36
38
|
"firewall",
|
|
37
|
-
"guardrails",
|
|
38
39
|
"cursor",
|
|
40
|
+
"secret-scanning",
|
|
41
|
+
"credential-exposure",
|
|
39
42
|
"llm-security"
|
|
40
43
|
],
|
|
41
44
|
"publishConfig": {
|