@chainpatrol/cli 0.8.0 → 0.10.0
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/CHANGELOG.md +28 -0
- package/README.md +26 -0
- package/dist/{breakdown-DXSN7KUF.js → breakdown-63FAOVL7.js} +1 -1
- package/dist/check-7QDINQPT.js +203 -0
- package/dist/{chunk-XIHOCIYM.js → chunk-BJISZ3CY.js} +1 -1
- package/dist/{chunk-ZN3VMRWG.js → chunk-R2DZGMGT.js} +59 -2
- package/dist/{chunk-F6D645LF.js → chunk-RIKR2WFT.js} +3 -0
- package/dist/{chunk-IUZB3DQW.js → chunk-Z76CUWSS.js} +11 -1
- package/dist/cli.js +68 -24
- package/dist/{completions-EGQIARFC.js → completions-D6SOLU2D.js} +1 -1
- package/dist/{configs-update-BBENU2PG.js → configs-update-2IOSKZH5.js} +1 -1
- package/dist/{create-SXPAFMPT.js → create-EWS3SFCH.js} +1 -1
- package/dist/{drift-WJD2Z5ZB.js → drift-Q3VG3XG3.js} +1 -1
- package/dist/{found-4UY3IC5T.js → found-22B7RZT5.js} +1 -1
- package/dist/{healthcheck-XTQN64DB.js → healthcheck-C3AIMUJT.js} +1 -1
- package/dist/{list-BVUG6RUF.js → list-AYHDFSQG.js} +1 -1
- package/dist/{list-2PBVBN5K.js → list-DNRWKM5O.js} +1 -1
- package/dist/{list-BGI7IZ55.js → list-HFWSMLGJ.js} +2 -2
- package/dist/{list-6ELSECNR.js → list-IA4CSOIY.js} +1 -1
- package/dist/{list-IJS66PYW.js → list-SDBLGBVW.js} +1 -1
- package/dist/{list-json-DWHMAA6S.js → list-json-CEPGVUGF.js} +1 -1
- package/dist/{run-M25F7TWI.js → run-AQMJRFGL.js} +1 -1
- package/dist/{run-3W7LSPX2.js → run-WJGHJPXN.js} +2 -2
- package/dist/{run-4S244KUM.js → run-YTWNQD5X.js} +1 -1
- package/dist/{setup-skill-J7PZYVCE.js → setup-skill-BLJLRCXL.js} +2 -2
- package/dist/{snapshot-MEOOARL3.js → snapshot-C5MZWJTW.js} +1 -1
- package/dist/{summary-JYDAPBYX.js → summary-NQDZQEOD.js} +1 -1
- package/dist/{validate-RI7YKHKH.js → validate-5DVPSXJP.js} +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @chainpatrol/cli
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 034096d: `chainpatrol asset check` now accepts multiple assets in a single
|
|
8
|
+
invocation — either as positional arguments
|
|
9
|
+
(`chainpatrol asset check a.example b.example c.example`) or via
|
|
10
|
+
repeated `--asset` flags. Lookups run in parallel (concurrency 10) and
|
|
11
|
+
the JSON output for bulk calls is
|
|
12
|
+
`{ results: [...], summary: { checked, blocked, allowed, unknown, errored } }`.
|
|
13
|
+
Single-asset JSON keeps the existing flat shape for back-compat. The
|
|
14
|
+
bundled `/chainpatrol` Claude Code skill is updated to call out the bulk
|
|
15
|
+
form so agents stop falling back to per-asset shell loops (or refusing
|
|
16
|
+
the request) when asked to check many domains at once.
|
|
17
|
+
|
|
18
|
+
## 0.9.0
|
|
19
|
+
|
|
20
|
+
### Minor Changes
|
|
21
|
+
|
|
22
|
+
- e2658e3: Add `chainpatrol asset check <content>` — look up a single URL, domain, or
|
|
23
|
+
crypto address against the ChainPatrol blocklist and external feeds and
|
|
24
|
+
return the aggregated status (`BLOCKED`, `ALLOWED`, or `UNKNOWN`) plus a
|
|
25
|
+
per-source breakdown. Supports `--json`, `--output markdown`, and
|
|
26
|
+
`--output csv` for agent/script use, and works with both device-code login
|
|
27
|
+
and `CHAINPATROL_API_KEY` env-var auth. The bundled `/chainpatrol` Claude
|
|
28
|
+
Code skill is updated to document the new command and to trigger on
|
|
29
|
+
phrases like "is this URL blocked" / "check this asset".
|
|
30
|
+
|
|
3
31
|
## 0.8.0
|
|
4
32
|
|
|
5
33
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ npm install -g chainpatrol
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
chainpatrol login
|
|
15
|
+
chainpatrol asset check https://phish.example
|
|
15
16
|
chainpatrol configs list --org my-org
|
|
16
17
|
chainpatrol --json configs list --org my-org
|
|
17
18
|
chainpatrol detections healthcheck --org my-org --run
|
|
@@ -38,6 +39,31 @@ chainpatrol detections drift --org my-org --explain --output markdown
|
|
|
38
39
|
chainpatrol queues snapshot --all --output csv
|
|
39
40
|
```
|
|
40
41
|
|
|
42
|
+
### Asset commands
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# look up a single asset against the ChainPatrol blocklist and external feeds
|
|
46
|
+
chainpatrol asset check https://phish.example
|
|
47
|
+
|
|
48
|
+
# bulk: check many assets in one call (parallel, concurrency=10)
|
|
49
|
+
chainpatrol asset check a.example b.example c.example
|
|
50
|
+
|
|
51
|
+
# repeated --asset is equivalent to positional args
|
|
52
|
+
chainpatrol asset check --asset a.example --asset b.example
|
|
53
|
+
|
|
54
|
+
# pipe a file of one-domain-per-line via xargs into a single CLI call
|
|
55
|
+
xargs -a domains.txt chainpatrol --json asset check
|
|
56
|
+
|
|
57
|
+
# JSON output for automation (single-asset returns flat shape; bulk returns
|
|
58
|
+
# { results: [...], summary: { checked, blocked, allowed, unknown, errored } })
|
|
59
|
+
chainpatrol --json asset check 0xabc123...
|
|
60
|
+
chainpatrol --json asset check a.example b.example c.example
|
|
61
|
+
|
|
62
|
+
# markdown / csv summary tables
|
|
63
|
+
chainpatrol asset check phish.example --output markdown
|
|
64
|
+
chainpatrol asset check a.example b.example --output csv
|
|
65
|
+
```
|
|
66
|
+
|
|
41
67
|
### Detection commands
|
|
42
68
|
|
|
43
69
|
```bash
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CliExitError,
|
|
3
|
+
ExitCode
|
|
4
|
+
} from "./chunk-E2LAMILJ.js";
|
|
5
|
+
import {
|
|
6
|
+
printOutput,
|
|
7
|
+
toCsvRows
|
|
8
|
+
} from "./chunk-VFT3TD3E.js";
|
|
9
|
+
import {
|
|
10
|
+
createApiClient
|
|
11
|
+
} from "./chunk-RIKR2WFT.js";
|
|
12
|
+
import "./chunk-EGWK6SRQ.js";
|
|
13
|
+
import "./chunk-TFCNKBRC.js";
|
|
14
|
+
import "./chunk-U73SABXK.js";
|
|
15
|
+
|
|
16
|
+
// src/commands/asset/check.ts
|
|
17
|
+
var DEFAULT_CONCURRENCY = 10;
|
|
18
|
+
function statusLine(result) {
|
|
19
|
+
const watchTag = result.watchStatus ? ` watch=${result.watchStatus}` : "";
|
|
20
|
+
const reasonTag = result.reason ? ` reason=${result.reason}` : "";
|
|
21
|
+
return `${result.status} (source=${result.source}${reasonTag}${watchTag})`;
|
|
22
|
+
}
|
|
23
|
+
async function runWithConcurrency(items, worker, concurrency) {
|
|
24
|
+
const results = new Array(items.length);
|
|
25
|
+
let cursor = 0;
|
|
26
|
+
const workers = new Array(Math.min(concurrency, items.length)).fill(null).map(async () => {
|
|
27
|
+
while (true) {
|
|
28
|
+
const index = cursor;
|
|
29
|
+
cursor += 1;
|
|
30
|
+
if (index >= items.length) return;
|
|
31
|
+
results[index] = await worker(items[index]);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
await Promise.all(workers);
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
async function runAssetCheck(options) {
|
|
38
|
+
const contents = (options.contents ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
39
|
+
if (contents.length === 0) {
|
|
40
|
+
throw new CliExitError(
|
|
41
|
+
"asset check requires at least one asset. Example: chainpatrol asset check https://example.com",
|
|
42
|
+
ExitCode.USAGE
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
|
|
46
|
+
const client = options.apiClient ?? createApiClient();
|
|
47
|
+
const perAssetResults = await runWithConcurrency(
|
|
48
|
+
contents,
|
|
49
|
+
async (content) => {
|
|
50
|
+
try {
|
|
51
|
+
const result = await client.assetCheck({ content });
|
|
52
|
+
return { content, ok: true, result };
|
|
53
|
+
} catch (err) {
|
|
54
|
+
return {
|
|
55
|
+
content,
|
|
56
|
+
ok: false,
|
|
57
|
+
error: err instanceof Error ? err.message : String(err)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
DEFAULT_CONCURRENCY
|
|
62
|
+
);
|
|
63
|
+
const summary = {
|
|
64
|
+
checked: perAssetResults.length,
|
|
65
|
+
blocked: perAssetResults.filter((r) => r.ok && r.result.status === "BLOCKED").length,
|
|
66
|
+
allowed: perAssetResults.filter((r) => r.ok && r.result.status === "ALLOWED").length,
|
|
67
|
+
unknown: perAssetResults.filter((r) => r.ok && r.result.status === "UNKNOWN").length,
|
|
68
|
+
errored: perAssetResults.filter((r) => !r.ok).length
|
|
69
|
+
};
|
|
70
|
+
const isSingle = contents.length === 1;
|
|
71
|
+
const jsonPayload = isSingle ? buildSingleJsonPayload(perAssetResults[0], options.explain) : {
|
|
72
|
+
results: perAssetResults.map(toJsonRow),
|
|
73
|
+
summary,
|
|
74
|
+
explanation: options.explain ? "Bulk asset check ran the public asset/check endpoint for each input in parallel (concurrency=10). Each row reports the aggregated ChainPatrol status plus the per-source breakdown." : void 0
|
|
75
|
+
};
|
|
76
|
+
const markdown = isSingle ? buildSingleMarkdown(perAssetResults[0]) : [
|
|
77
|
+
`# Asset Check (${summary.checked})`,
|
|
78
|
+
"",
|
|
79
|
+
`- Blocked: ${summary.blocked}`,
|
|
80
|
+
`- Allowed: ${summary.allowed}`,
|
|
81
|
+
`- Unknown: ${summary.unknown}`,
|
|
82
|
+
...summary.errored > 0 ? [`- Errored: ${summary.errored}`] : [],
|
|
83
|
+
"",
|
|
84
|
+
"| Content | Status | Source | Reason | Watch |",
|
|
85
|
+
"| --- | --- | --- | --- | --- |",
|
|
86
|
+
...perAssetResults.map(toMarkdownRow)
|
|
87
|
+
].join("\n");
|
|
88
|
+
const csv = toCsvRows(perAssetResults.map(toCsvRow));
|
|
89
|
+
printOutput({
|
|
90
|
+
outputFormat,
|
|
91
|
+
json: jsonPayload,
|
|
92
|
+
markdown,
|
|
93
|
+
csv,
|
|
94
|
+
human: () => {
|
|
95
|
+
for (const entry of perAssetResults) {
|
|
96
|
+
if (entry.ok) {
|
|
97
|
+
console.log(`${entry.content} -> ${statusLine(entry.result)}`);
|
|
98
|
+
} else {
|
|
99
|
+
console.log(`${entry.content} -> ERROR: ${entry.error}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (isSingle && perAssetResults[0].ok) {
|
|
103
|
+
const sources = perAssetResults[0].result.sources;
|
|
104
|
+
if (sources.length > 0) {
|
|
105
|
+
console.log("Sources:");
|
|
106
|
+
for (const entry of sources) {
|
|
107
|
+
console.log(` - ${entry.source}: ${entry.status}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (perAssetResults[0].result.message) {
|
|
111
|
+
console.log(`Message: ${perAssetResults[0].result.message}`);
|
|
112
|
+
}
|
|
113
|
+
if (perAssetResults[0].result.code) {
|
|
114
|
+
console.log(`Code: ${perAssetResults[0].result.code}`);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
console.log(
|
|
118
|
+
`Summary: ${summary.checked} checked \u2014 blocked=${summary.blocked} allowed=${summary.allowed} unknown=${summary.unknown}${summary.errored > 0 ? ` errored=${summary.errored}` : ""}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
if (options.explain) {
|
|
122
|
+
console.log(
|
|
123
|
+
"Status is the aggregated verdict across ChainPatrol and external feeds; see the per-source rows for the underlying signals."
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
if (summary.errored > 0) {
|
|
129
|
+
throw new CliExitError(
|
|
130
|
+
`asset check: ${summary.errored} of ${summary.checked} request(s) failed.`,
|
|
131
|
+
ExitCode.UNKNOWN
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function buildSingleJsonPayload(entry, explain) {
|
|
136
|
+
if (!entry.ok) {
|
|
137
|
+
return {
|
|
138
|
+
content: entry.content,
|
|
139
|
+
error: entry.error,
|
|
140
|
+
explanation: explain ? "asset check failed for this asset. Inspect the error message and retry." : void 0
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
content: entry.content,
|
|
145
|
+
...entry.result,
|
|
146
|
+
explanation: explain ? "Asset check aggregates ChainPatrol records and external sources to classify a domain, URL, or crypto address as BLOCKED, ALLOWED, or UNKNOWN." : void 0
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function buildSingleMarkdown(entry) {
|
|
150
|
+
if (!entry.ok) {
|
|
151
|
+
return [`# Asset Check: ${entry.content}`, "", `- Error: ${entry.error}`].join("\n");
|
|
152
|
+
}
|
|
153
|
+
const result = entry.result;
|
|
154
|
+
return [
|
|
155
|
+
`# Asset Check: ${entry.content}`,
|
|
156
|
+
"",
|
|
157
|
+
`- Status: **${result.status}**`,
|
|
158
|
+
`- Source: ${result.source}`,
|
|
159
|
+
...result.reason ? [`- Reason: ${result.reason}`] : [],
|
|
160
|
+
...result.watchStatus ? [`- Watch status: ${result.watchStatus}`] : [],
|
|
161
|
+
...result.message ? [`- Message: ${result.message}`] : [],
|
|
162
|
+
...result.code ? [`- Code: ${result.code}`] : [],
|
|
163
|
+
"",
|
|
164
|
+
"## Per-source results",
|
|
165
|
+
"",
|
|
166
|
+
...result.sources.map((entry2) => `- ${entry2.source}: ${entry2.status}`)
|
|
167
|
+
].join("\n");
|
|
168
|
+
}
|
|
169
|
+
function toJsonRow(entry) {
|
|
170
|
+
if (!entry.ok) {
|
|
171
|
+
return { content: entry.content, error: entry.error };
|
|
172
|
+
}
|
|
173
|
+
return { content: entry.content, ...entry.result };
|
|
174
|
+
}
|
|
175
|
+
function toMarkdownRow(entry) {
|
|
176
|
+
if (!entry.ok) {
|
|
177
|
+
return `| ${entry.content} | ERROR | \u2014 | ${entry.error.replace(/\|/g, "\\|")} | \u2014 |`;
|
|
178
|
+
}
|
|
179
|
+
const r = entry.result;
|
|
180
|
+
return `| ${entry.content} | ${r.status} | ${r.source} | ${r.reason ?? ""} | ${r.watchStatus ?? ""} |`;
|
|
181
|
+
}
|
|
182
|
+
function toCsvRow(entry) {
|
|
183
|
+
if (!entry.ok) {
|
|
184
|
+
return {
|
|
185
|
+
content: entry.content,
|
|
186
|
+
status: "ERROR",
|
|
187
|
+
source: "",
|
|
188
|
+
reason: entry.error,
|
|
189
|
+
watchStatus: ""
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const r = entry.result;
|
|
193
|
+
return {
|
|
194
|
+
content: entry.content,
|
|
195
|
+
status: r.status,
|
|
196
|
+
source: r.source,
|
|
197
|
+
reason: r.reason ?? "",
|
|
198
|
+
watchStatus: r.watchStatus ?? ""
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
export {
|
|
202
|
+
runAssetCheck
|
|
203
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
installCompletions,
|
|
3
3
|
uninstallCompletions
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Z76CUWSS.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/setup-skill.ts
|
|
7
7
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, readFileSync as readFileSync3, rmSync } from "fs";
|
|
@@ -196,7 +196,9 @@ description: |
|
|
|
196
196
|
"org healthcheck", "organization health check", "audit my org",
|
|
197
197
|
"what's wrong with org", "review org setup", "list orgs", "list organizations",
|
|
198
198
|
"orgs with takedowns off", "automation off across orgs",
|
|
199
|
-
"which customers have X enabled", "service toggles by org"
|
|
199
|
+
"which customers have X enabled", "service toggles by org",
|
|
200
|
+
"is this URL blocked", "is this domain blocked", "is this address blocked",
|
|
201
|
+
"check this asset", "asset check", "lookup asset status".
|
|
200
202
|
allowed-tools:
|
|
201
203
|
- Bash
|
|
202
204
|
- Read
|
|
@@ -310,6 +312,61 @@ so the same background+tail pattern works without \`--json\`. Prefer
|
|
|
310
312
|
chainpatrol logout
|
|
311
313
|
\`\`\`
|
|
312
314
|
|
|
315
|
+
### \`asset check\` \u2014 Check one or many assets against the blocklist
|
|
316
|
+
|
|
317
|
+
Look up a URL, domain, or crypto address and return its aggregated status
|
|
318
|
+
(\`BLOCKED\`, \`ALLOWED\`, or \`UNKNOWN\`) plus a per-source breakdown
|
|
319
|
+
(ChainPatrol + external feeds like eth-phishing-detect, phishfort, seal,
|
|
320
|
+
polkadot-phishing). Works whether you're authenticated via device-code
|
|
321
|
+
login or via a \`CHAINPATROL_API_KEY\` env var.
|
|
322
|
+
|
|
323
|
+
Single asset:
|
|
324
|
+
|
|
325
|
+
\`\`\`bash
|
|
326
|
+
chainpatrol asset check https://phish.example
|
|
327
|
+
chainpatrol asset check 0xabc123...
|
|
328
|
+
\`\`\`
|
|
329
|
+
|
|
330
|
+
#### Bulk checks (preferred for >1 asset)
|
|
331
|
+
|
|
332
|
+
Pass multiple assets in a single invocation \u2014 the CLI runs them in
|
|
333
|
+
parallel (concurrency 10) and returns one row per asset. **Do this
|
|
334
|
+
instead of looping the CLI in a shell \`for\` loop**: one process, one
|
|
335
|
+
auth handshake, parallel HTTP. Use either positional args or repeated
|
|
336
|
+
\`--asset\`:
|
|
337
|
+
|
|
338
|
+
\`\`\`bash
|
|
339
|
+
# positional form
|
|
340
|
+
chainpatrol asset check a.example b.example c.example
|
|
341
|
+
|
|
342
|
+
# repeated --asset (handy when content has spaces or special chars)
|
|
343
|
+
chainpatrol asset check --asset a.example --asset b.example
|
|
344
|
+
|
|
345
|
+
# from a file of one-asset-per-line (use xargs to splat into one call)
|
|
346
|
+
xargs -a domains.txt chainpatrol asset check
|
|
347
|
+
\`\`\`
|
|
348
|
+
|
|
349
|
+
JSON mode is the agent-friendly default \u2014 single-asset JSON keeps the
|
|
350
|
+
flat \`{ content, status, source, reason?, sources[], watchStatus? }\`
|
|
351
|
+
shape; multi-asset JSON returns \`{ results: [...], summary: { checked,
|
|
352
|
+
blocked, allowed, unknown, errored } }\`:
|
|
353
|
+
|
|
354
|
+
\`\`\`bash
|
|
355
|
+
chainpatrol --json asset check https://phish.example
|
|
356
|
+
chainpatrol --json asset check a.example b.example c.example
|
|
357
|
+
\`\`\`
|
|
358
|
+
|
|
359
|
+
Markdown / CSV are also available for sharing in docs / chat:
|
|
360
|
+
|
|
361
|
+
\`\`\`bash
|
|
362
|
+
chainpatrol asset check phish.example --output markdown
|
|
363
|
+
chainpatrol asset check a.example b.example --output csv
|
|
364
|
+
\`\`\`
|
|
365
|
+
|
|
366
|
+
If any individual lookup fails, the CLI still prints results for the
|
|
367
|
+
successful ones, then exits non-zero so failures aren't silently
|
|
368
|
+
swallowed.
|
|
369
|
+
|
|
313
370
|
### \`configs list\` \u2014 List detection configurations
|
|
314
371
|
|
|
315
372
|
Requires authentication and an organization slug.
|
|
@@ -16,6 +16,7 @@ _chainpatrol() {
|
|
|
16
16
|
commands=(
|
|
17
17
|
'login:Authenticate with ChainPatrol'
|
|
18
18
|
'logout:Clear stored credentials'
|
|
19
|
+
'asset:Check asset status against ChainPatrol'
|
|
19
20
|
'configs:Manage detection configs'
|
|
20
21
|
'detections:Validate and run detection configs'
|
|
21
22
|
'metrics:Query organization metrics'
|
|
@@ -56,6 +57,11 @@ _chainpatrol() {
|
|
|
56
57
|
_arguments -s $global_flags
|
|
57
58
|
elif (( CURRENT == 3 )); then
|
|
58
59
|
case "\${words[2]}" in
|
|
60
|
+
asset)
|
|
61
|
+
local -a asset_subcommands
|
|
62
|
+
asset_subcommands=('check:Check asset status against ChainPatrol')
|
|
63
|
+
_describe 'subcommand' asset_subcommands
|
|
64
|
+
;;
|
|
59
65
|
configs)
|
|
60
66
|
local -a subcommands
|
|
61
67
|
subcommands=('list:List detection configurations')
|
|
@@ -118,13 +124,17 @@ var BASH_COMPLETION = `_chainpatrol() {
|
|
|
118
124
|
COMPREPLY=()
|
|
119
125
|
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
120
126
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
121
|
-
commands="login logout configs detections metrics reports queues presets setup uninstall completions help"
|
|
127
|
+
commands="login logout asset configs detections metrics reports queues presets setup uninstall completions help"
|
|
122
128
|
|
|
123
129
|
case "\${prev}" in
|
|
124
130
|
chainpatrol)
|
|
125
131
|
COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
|
|
126
132
|
return 0
|
|
127
133
|
;;
|
|
134
|
+
asset)
|
|
135
|
+
COMPREPLY=( $(compgen -W "check" -- "\${cur}") )
|
|
136
|
+
return 0
|
|
137
|
+
;;
|
|
128
138
|
configs)
|
|
129
139
|
COMPREPLY=( $(compgen -W "list" -- "\${cur}") )
|
|
130
140
|
return 0
|
package/dist/cli.js
CHANGED
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
getCliVersion,
|
|
14
14
|
isSkillInstalled,
|
|
15
15
|
readInstalledSkillVersion
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-R2DZGMGT.js";
|
|
17
|
+
import "./chunk-Z76CUWSS.js";
|
|
18
18
|
import {
|
|
19
19
|
DateTime
|
|
20
20
|
} from "./chunk-TFCNKBRC.js";
|
|
@@ -91,6 +91,29 @@ var HELP = {
|
|
|
91
91
|
description: "Clear stored credentials from this machine.",
|
|
92
92
|
usage: "chainpatrol logout"
|
|
93
93
|
},
|
|
94
|
+
asset: {
|
|
95
|
+
description: "Check whether one or more assets are BLOCKED, ALLOWED, or UNKNOWN.",
|
|
96
|
+
usage: "chainpatrol asset check <content> [<content> ...]",
|
|
97
|
+
examples: [
|
|
98
|
+
"chainpatrol asset check https://phish.example",
|
|
99
|
+
"chainpatrol asset check a.example b.example c.example",
|
|
100
|
+
"chainpatrol --json asset check 0xabc123\u2026"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"asset check": {
|
|
104
|
+
description: "Look up one or more assets (URL, domain, or crypto address) against the ChainPatrol blocklist and external feeds. Returns the aggregated status plus a per-source breakdown. Multiple assets can be passed as positional arguments or via repeated --asset; they are checked in parallel (concurrency 10).",
|
|
105
|
+
usage: "chainpatrol asset check <content> [<content> ...]",
|
|
106
|
+
options: [
|
|
107
|
+
"--asset <content> Asset content (repeatable; alternative to positional args)"
|
|
108
|
+
],
|
|
109
|
+
examples: [
|
|
110
|
+
"chainpatrol asset check https://phish.example",
|
|
111
|
+
"chainpatrol asset check a.example b.example c.example --json",
|
|
112
|
+
"chainpatrol asset check --asset a.example --asset b.example --output csv",
|
|
113
|
+
"chainpatrol asset check phish.example --output markdown",
|
|
114
|
+
"chainpatrol --json asset check 0xabc123\u2026"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
94
117
|
configs: {
|
|
95
118
|
description: "Manage detection configs for an organization.",
|
|
96
119
|
usage: "chainpatrol configs <list>",
|
|
@@ -391,6 +414,7 @@ function getTopLevelHelp() {
|
|
|
391
414
|
" Commands",
|
|
392
415
|
" login Authenticate with ChainPatrol",
|
|
393
416
|
" logout Clear stored credentials",
|
|
417
|
+
" asset Check asset status against ChainPatrol",
|
|
394
418
|
" configs Manage detection configs",
|
|
395
419
|
" detections Validate, run, update detection configs",
|
|
396
420
|
" healthchecks List and run organization healthchecks",
|
|
@@ -561,6 +585,7 @@ function printVersionMessages(messages) {
|
|
|
561
585
|
var COMMANDS = [
|
|
562
586
|
"login",
|
|
563
587
|
"logout",
|
|
588
|
+
"asset",
|
|
564
589
|
"configs",
|
|
565
590
|
"detections",
|
|
566
591
|
"healthchecks",
|
|
@@ -754,12 +779,12 @@ function parseAttachmentUrls() {
|
|
|
754
779
|
}
|
|
755
780
|
async function handleConfigsList(org) {
|
|
756
781
|
if (jsonMode) {
|
|
757
|
-
const { listConfigsJson } = await import("./list-json-
|
|
782
|
+
const { listConfigsJson } = await import("./list-json-CEPGVUGF.js");
|
|
758
783
|
await listConfigsJson({ org });
|
|
759
784
|
return;
|
|
760
785
|
}
|
|
761
786
|
const { render } = await import("ink");
|
|
762
|
-
const { default: ConfigsList } = await import("./list-
|
|
787
|
+
const { default: ConfigsList } = await import("./list-IA4CSOIY.js");
|
|
763
788
|
const { default: React } = await import("react");
|
|
764
789
|
render(React.createElement(ConfigsList, { org }));
|
|
765
790
|
}
|
|
@@ -854,10 +879,29 @@ async function main() {
|
|
|
854
879
|
}
|
|
855
880
|
throw new Error("Usage: chainpatrol configs list");
|
|
856
881
|
}
|
|
882
|
+
case "asset": {
|
|
883
|
+
if (subcommand === "check") {
|
|
884
|
+
const positionals = cli.input.slice(2);
|
|
885
|
+
const flagAssets = parseAssetInputs();
|
|
886
|
+
const contents = [...positionals, ...flagAssets];
|
|
887
|
+
const { runAssetCheck } = await import("./check-7QDINQPT.js");
|
|
888
|
+
await runAssetCheck({
|
|
889
|
+
contents,
|
|
890
|
+
json: jsonMode,
|
|
891
|
+
outputFormat: cliContext.outputFormat,
|
|
892
|
+
explain: cliContext.explain
|
|
893
|
+
});
|
|
894
|
+
break;
|
|
895
|
+
}
|
|
896
|
+
const hint = subcommand ? suggest(subcommand, ["check"]) : null;
|
|
897
|
+
throw new Error(
|
|
898
|
+
subcommand ? `Unknown subcommand: asset ${subcommand}${hint ? `. Did you mean "asset ${hint}"?` : ""}` : "Usage: chainpatrol asset check <content> [<content> ...]"
|
|
899
|
+
);
|
|
900
|
+
}
|
|
857
901
|
case "detections": {
|
|
858
902
|
const org = await resolveOrg();
|
|
859
903
|
if (subcommand === "healthcheck") {
|
|
860
|
-
const { runDetectionsHealthcheck } = await import("./healthcheck-
|
|
904
|
+
const { runDetectionsHealthcheck } = await import("./healthcheck-C3AIMUJT.js");
|
|
861
905
|
await runDetectionsHealthcheck({
|
|
862
906
|
org,
|
|
863
907
|
source: cli.flags.source,
|
|
@@ -872,7 +916,7 @@ async function main() {
|
|
|
872
916
|
break;
|
|
873
917
|
}
|
|
874
918
|
if (subcommand === "validate") {
|
|
875
|
-
const { runDetectionsValidate } = await import("./validate-
|
|
919
|
+
const { runDetectionsValidate } = await import("./validate-5DVPSXJP.js");
|
|
876
920
|
await runDetectionsValidate({
|
|
877
921
|
org,
|
|
878
922
|
source: cli.flags.source,
|
|
@@ -887,7 +931,7 @@ async function main() {
|
|
|
887
931
|
break;
|
|
888
932
|
}
|
|
889
933
|
if (subcommand === "drift") {
|
|
890
|
-
const { runDetectionsDrift } = await import("./drift-
|
|
934
|
+
const { runDetectionsDrift } = await import("./drift-Q3VG3XG3.js");
|
|
891
935
|
await runDetectionsDrift({
|
|
892
936
|
org,
|
|
893
937
|
source: cli.flags.source,
|
|
@@ -901,7 +945,7 @@ async function main() {
|
|
|
901
945
|
break;
|
|
902
946
|
}
|
|
903
947
|
if (subcommand === "run") {
|
|
904
|
-
const { runDetectionsRun } = await import("./run-
|
|
948
|
+
const { runDetectionsRun } = await import("./run-YTWNQD5X.js");
|
|
905
949
|
await runDetectionsRun({
|
|
906
950
|
org,
|
|
907
951
|
configId: cli.flags.configId,
|
|
@@ -920,7 +964,7 @@ async function main() {
|
|
|
920
964
|
break;
|
|
921
965
|
}
|
|
922
966
|
if (action === "run") {
|
|
923
|
-
const { runDetectionsRun } = await import("./run-
|
|
967
|
+
const { runDetectionsRun } = await import("./run-YTWNQD5X.js");
|
|
924
968
|
await runDetectionsRun({
|
|
925
969
|
org,
|
|
926
970
|
configId: cli.flags.configId,
|
|
@@ -938,7 +982,7 @@ async function main() {
|
|
|
938
982
|
throw new Error("detections configs update requires --config-id");
|
|
939
983
|
}
|
|
940
984
|
const configPatch = getConfigPatchFromSetFlags();
|
|
941
|
-
const { runDetectionsConfigsUpdate } = await import("./configs-update-
|
|
985
|
+
const { runDetectionsConfigsUpdate } = await import("./configs-update-2IOSKZH5.js");
|
|
942
986
|
await runDetectionsConfigsUpdate({
|
|
943
987
|
org,
|
|
944
988
|
configId: cli.flags.configId,
|
|
@@ -965,7 +1009,7 @@ async function main() {
|
|
|
965
1009
|
case "metrics": {
|
|
966
1010
|
const org = await resolveOrg();
|
|
967
1011
|
if (subcommand === "summary") {
|
|
968
|
-
const { runMetricsSummary } = await import("./summary-
|
|
1012
|
+
const { runMetricsSummary } = await import("./summary-NQDZQEOD.js");
|
|
969
1013
|
await runMetricsSummary({
|
|
970
1014
|
org,
|
|
971
1015
|
from: cli.flags.from,
|
|
@@ -977,7 +1021,7 @@ async function main() {
|
|
|
977
1021
|
break;
|
|
978
1022
|
}
|
|
979
1023
|
if (subcommand === "found") {
|
|
980
|
-
const { runMetricsFound } = await import("./found-
|
|
1024
|
+
const { runMetricsFound } = await import("./found-22B7RZT5.js");
|
|
981
1025
|
await runMetricsFound({
|
|
982
1026
|
org,
|
|
983
1027
|
from: cli.flags.from,
|
|
@@ -994,7 +1038,7 @@ async function main() {
|
|
|
994
1038
|
if (!by || !["day", "type", "brand"].includes(by)) {
|
|
995
1039
|
throw new Error("metrics breakdown requires --by <day|type|brand>");
|
|
996
1040
|
}
|
|
997
|
-
const { runMetricsBreakdown } = await import("./breakdown-
|
|
1041
|
+
const { runMetricsBreakdown } = await import("./breakdown-63FAOVL7.js");
|
|
998
1042
|
await runMetricsBreakdown({
|
|
999
1043
|
org,
|
|
1000
1044
|
by,
|
|
@@ -1014,7 +1058,7 @@ async function main() {
|
|
|
1014
1058
|
case "reports": {
|
|
1015
1059
|
if (subcommand === "list") {
|
|
1016
1060
|
const org = await resolveOrg();
|
|
1017
|
-
const { runReportsList } = await import("./list-
|
|
1061
|
+
const { runReportsList } = await import("./list-AYHDFSQG.js");
|
|
1018
1062
|
await runReportsList({
|
|
1019
1063
|
org,
|
|
1020
1064
|
limit: cli.flags.limit,
|
|
@@ -1030,7 +1074,7 @@ async function main() {
|
|
|
1030
1074
|
}
|
|
1031
1075
|
if (subcommand === "create") {
|
|
1032
1076
|
const org = await tryResolveOrg();
|
|
1033
|
-
const { runReportsCreate } = await import("./create-
|
|
1077
|
+
const { runReportsCreate } = await import("./create-EWS3SFCH.js");
|
|
1034
1078
|
await runReportsCreate({
|
|
1035
1079
|
org,
|
|
1036
1080
|
title: cli.flags.title,
|
|
@@ -1054,7 +1098,7 @@ async function main() {
|
|
|
1054
1098
|
}
|
|
1055
1099
|
case "queues": {
|
|
1056
1100
|
if (subcommand === "snapshot") {
|
|
1057
|
-
const { runQueuesSnapshot } = await import("./snapshot-
|
|
1101
|
+
const { runQueuesSnapshot } = await import("./snapshot-C5MZWJTW.js");
|
|
1058
1102
|
await runQueuesSnapshot({
|
|
1059
1103
|
org: cli.flags.org,
|
|
1060
1104
|
all: cli.flags.all,
|
|
@@ -1072,7 +1116,7 @@ async function main() {
|
|
|
1072
1116
|
}
|
|
1073
1117
|
case "orgs": {
|
|
1074
1118
|
if (subcommand === "list") {
|
|
1075
|
-
const { runOrgsList } = await import("./list-
|
|
1119
|
+
const { runOrgsList } = await import("./list-SDBLGBVW.js");
|
|
1076
1120
|
await runOrgsList({
|
|
1077
1121
|
query: cli.flags.query,
|
|
1078
1122
|
subscriptionStatus: cli.flags.subscriptionStatus,
|
|
@@ -1092,7 +1136,7 @@ async function main() {
|
|
|
1092
1136
|
}
|
|
1093
1137
|
case "healthchecks": {
|
|
1094
1138
|
if (subcommand === "list") {
|
|
1095
|
-
const { runHealthchecksList } = await import("./list-
|
|
1139
|
+
const { runHealthchecksList } = await import("./list-DNRWKM5O.js");
|
|
1096
1140
|
await runHealthchecksList({
|
|
1097
1141
|
json: jsonMode,
|
|
1098
1142
|
outputFormat: cliContext.outputFormat
|
|
@@ -1106,7 +1150,7 @@ async function main() {
|
|
|
1106
1150
|
thresholds.minResults = cli.flags.minResults;
|
|
1107
1151
|
if (cli.flags.lookbackHours !== void 0)
|
|
1108
1152
|
thresholds.lookbackHours = cli.flags.lookbackHours;
|
|
1109
|
-
const { runHealthchecksRun } = await import("./run-
|
|
1153
|
+
const { runHealthchecksRun } = await import("./run-AQMJRFGL.js");
|
|
1110
1154
|
await runHealthchecksRun({
|
|
1111
1155
|
org,
|
|
1112
1156
|
id: action,
|
|
@@ -1124,7 +1168,7 @@ async function main() {
|
|
|
1124
1168
|
}
|
|
1125
1169
|
case "presets": {
|
|
1126
1170
|
if (subcommand === "list") {
|
|
1127
|
-
const { runPresetsList } = await import("./list-
|
|
1171
|
+
const { runPresetsList } = await import("./list-HFWSMLGJ.js");
|
|
1128
1172
|
await runPresetsList({ outputFormat: cliContext.outputFormat });
|
|
1129
1173
|
break;
|
|
1130
1174
|
}
|
|
@@ -1135,7 +1179,7 @@ async function main() {
|
|
|
1135
1179
|
);
|
|
1136
1180
|
}
|
|
1137
1181
|
const org = await resolveOrg();
|
|
1138
|
-
const { runPresetsRun } = await import("./run-
|
|
1182
|
+
const { runPresetsRun } = await import("./run-WJGHJPXN.js");
|
|
1139
1183
|
await runPresetsRun({
|
|
1140
1184
|
presetId: action,
|
|
1141
1185
|
org,
|
|
@@ -1152,17 +1196,17 @@ async function main() {
|
|
|
1152
1196
|
case "setup":
|
|
1153
1197
|
case "install":
|
|
1154
1198
|
case "i": {
|
|
1155
|
-
const { setupSkill } = await import("./setup-skill-
|
|
1199
|
+
const { setupSkill } = await import("./setup-skill-BLJLRCXL.js");
|
|
1156
1200
|
setupSkill({ json: jsonMode, cloud: cli.flags.cloud });
|
|
1157
1201
|
break;
|
|
1158
1202
|
}
|
|
1159
1203
|
case "uninstall": {
|
|
1160
|
-
const { uninstallSkill } = await import("./setup-skill-
|
|
1204
|
+
const { uninstallSkill } = await import("./setup-skill-BLJLRCXL.js");
|
|
1161
1205
|
uninstallSkill({ json: jsonMode });
|
|
1162
1206
|
break;
|
|
1163
1207
|
}
|
|
1164
1208
|
case "completions": {
|
|
1165
|
-
const { printCompletions } = await import("./completions-
|
|
1209
|
+
const { printCompletions } = await import("./completions-D6SOLU2D.js");
|
|
1166
1210
|
printCompletions(subcommand);
|
|
1167
1211
|
break;
|
|
1168
1212
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRESETS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BJISZ3CY.js";
|
|
4
4
|
import "./chunk-E2LAMILJ.js";
|
|
5
5
|
import {
|
|
6
6
|
printOutput,
|
|
7
7
|
toCsvRows
|
|
8
8
|
} from "./chunk-VFT3TD3E.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-RIKR2WFT.js";
|
|
10
10
|
import "./chunk-EGWK6SRQ.js";
|
|
11
11
|
import "./chunk-TFCNKBRC.js";
|
|
12
12
|
import "./chunk-U73SABXK.js";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getPresetDefinition,
|
|
3
3
|
runPreset
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BJISZ3CY.js";
|
|
5
5
|
import {
|
|
6
6
|
CliExitError,
|
|
7
7
|
ExitCode
|
|
8
8
|
} from "./chunk-E2LAMILJ.js";
|
|
9
9
|
import "./chunk-VFT3TD3E.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-RIKR2WFT.js";
|
|
11
11
|
import "./chunk-EGWK6SRQ.js";
|
|
12
12
|
import "./chunk-TFCNKBRC.js";
|
|
13
13
|
import "./chunk-U73SABXK.js";
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
readInstalledSkillVersion,
|
|
7
7
|
setupSkill,
|
|
8
8
|
uninstallSkill
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-R2DZGMGT.js";
|
|
10
|
+
import "./chunk-Z76CUWSS.js";
|
|
11
11
|
export {
|
|
12
12
|
getBundledSkillContent,
|
|
13
13
|
getBundledSkillVersion,
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@chainpatrol/cli",
|
|
3
3
|
"description": "The official ChainPatrol CLI — terminal interface for threat detection",
|
|
4
4
|
"author": "Umar Ahmed <umar@chainpatrol.io>",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.10.0",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"homepage": "https://chainpatrol.com/docs/cli",
|
|
8
8
|
"keywords": [
|