@chainpatrol/cli 1.4.0 → 1.6.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 +54 -0
- package/dist/{breakdown-5DJMXRK4.js → breakdown-2HAZ2MX5.js} +1 -1
- package/dist/{check-5P3EXVSN.js → check-5LJI7N5I.js} +1 -1
- package/dist/{chunk-OFUIYMPD.js → chunk-322AVMUH.js} +4 -0
- package/dist/{chunk-MM5OAY2U.js → chunk-55B34HJK.js} +1 -1
- package/dist/{chunk-PEX2A3IL.js → chunk-FPCN4SNK.js} +1 -1
- package/dist/{chunk-6VTWWGNV.js → chunk-P5DJRDMV.js} +11 -1
- package/dist/cli.js +114 -41
- package/dist/{completions-ILC76OX5.js → completions-FEUVEC5N.js} +1 -1
- package/dist/{configs-update-TAYT4JTU.js → configs-update-N7ZIWWKQ.js} +1 -1
- package/dist/{create-UXRULUR7.js → create-4ZJ7DXHX.js} +1 -1
- package/dist/{drift-TCZELVHO.js → drift-BGWNH6QE.js} +1 -1
- package/dist/{found-NCP6H2SD.js → found-QOARB5G2.js} +1 -1
- package/dist/{get-SM5KGJVA.js → get-ON5JOCV7.js} +1 -1
- package/dist/{healthcheck-5YEZCTXC.js → healthcheck-34LQTSVZ.js} +1 -1
- package/dist/{list-2RHEVI2T.js → list-6CAKS43I.js} +1 -1
- package/dist/{list-WQBXC3QS.js → list-AG6LWNMN.js} +1 -1
- package/dist/{list-W6JZUNS2.js → list-DJFIKDY6.js} +1 -1
- package/dist/{list-MPSB2DYU.js → list-H6QWWXOU.js} +1 -1
- package/dist/{list-4YBWU2JZ.js → list-IVFZGLKN.js} +1 -1
- package/dist/{list-23C72EH4.js → list-JG5ARDTR.js} +2 -2
- package/dist/{list-BAPTZNTM.js → list-MHOHKFXT.js} +1 -1
- package/dist/{list-TA4HYD66.js → list-PAJBOVN2.js} +1 -1
- package/dist/{list-QLMFMYU6.js → list-SJFJT4V3.js} +1 -1
- package/dist/{list-SCA73PLA.js → list-UPY6PHY4.js} +1 -1
- package/dist/{list-GEJQ6N2Y.js → list-UXYGISAK.js} +1 -1
- package/dist/{list-QJYRXPXM.js → list-W6CF25ZS.js} +1 -1
- package/dist/{list-json-NJ5IFRDE.js → list-json-ZR3XOQPJ.js} +1 -1
- package/dist/{organization-N6RMWKJM.js → organization-YAID36GZ.js} +1 -1
- package/dist/review-7A7WV73T.js +270 -0
- package/dist/{run-RRG7D4SN.js → run-AIYPHWFJ.js} +1 -1
- package/dist/{run-KOZXEYTL.js → run-LZZOV4HG.js} +1 -1
- package/dist/{run-TUADYLU7.js → run-YP4O5VXZ.js} +2 -2
- package/dist/{search-M6EKPO4D.js → search-OGGKNPUJ.js} +1 -1
- package/dist/{search-Y37B2JVP.js → search-W5QM2IOC.js} +1 -1
- package/dist/{setup-skill-RRTDEOR4.js → setup-skill-2KCAXU2U.js} +2 -2
- package/dist/{snapshot-RFGSAHO7.js → snapshot-TLSOPK7C.js} +1 -1
- package/dist/{summary-XCN3DRYP.js → summary-XA3PVJIW.js} +1 -1
- package/dist/{validate-OYTNB2ZM.js → validate-WB5367QN.js} +1 -1
- package/dist/{whoami-5WCW3FOZ.js → whoami-YE3V6I65.js} +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @chainpatrol/cli
|
|
2
2
|
|
|
3
|
+
## 1.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9b15056: Adds `chainpatrol proposals review` for actioning proposals from the terminal.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
chainpatrol proposals review 4321 --asset-id 987 --decision approve --label "General Phishing"
|
|
11
|
+
chainpatrol proposals review 4321 --asset-id 987 --decision reject --reject-reason irrelevant
|
|
12
|
+
chainpatrol proposals review 4321 --asset-id 987 --decision watchlist --watchlist-reason DEAD
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Take both IDs from `proposals[].id` and `proposals[].asset.id` in
|
|
16
|
+
`reports list --needs-customer-review --json`. `--asset-id` is required and must
|
|
17
|
+
match the proposal's current asset.
|
|
18
|
+
|
|
19
|
+
A review cannot be undone, so the command prints the decision it is about to
|
|
20
|
+
submit and waits for `yes`. `--yes` skips the prompt and is required with
|
|
21
|
+
`--json`, `--no-input`, or a non-TTY stdin. `--dry-run` prints the payload and
|
|
22
|
+
submits nothing.
|
|
23
|
+
|
|
24
|
+
`--label` is required when approving, plus `--brand-id` for the labels that name
|
|
25
|
+
an impersonation target; `--reject-reason` when rejecting; `--watchlist-reason`
|
|
26
|
+
when watchlisting; and `--note` when that reason is `OTHER`. Decisions and
|
|
27
|
+
reasons are case-insensitive.
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- Updated dependencies [a253313]
|
|
32
|
+
- Updated dependencies [108f9f8]
|
|
33
|
+
- @chainpatrol/sdk@1.6.0
|
|
34
|
+
|
|
35
|
+
## 1.5.0
|
|
36
|
+
|
|
37
|
+
### Minor Changes
|
|
38
|
+
|
|
39
|
+
- 5427cd8: Fixes `needsCustomerReview` (`--needs-customer-review`) so it returns the same
|
|
40
|
+
queue the organization's Review page shows. It previously treated "Obligatory
|
|
41
|
+
Organization Admin Approval" as "waiting on the customer", which for an org with
|
|
42
|
+
that toggle on returned every report with a pending proposal — a proposal
|
|
43
|
+
ChainPatrol has not triaged yet is still waiting on our review, and reaches the
|
|
44
|
+
customer only once staff escalate it. Reports the organization submitted itself
|
|
45
|
+
are now included, matching the Review page, instead of being excluded.
|
|
46
|
+
|
|
47
|
+
The predicate now lives in one place (`customerReviewQueueSql` in the private
|
|
48
|
+
`@chainpatrol/core` package) shared with the Review page, the customer view of
|
|
49
|
+
the Reports page, and the staff review-queue counts, so the definitions can no
|
|
50
|
+
longer drift.
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- Updated dependencies [5427cd8]
|
|
55
|
+
- @chainpatrol/sdk@1.5.0
|
|
56
|
+
|
|
3
57
|
## 1.4.0
|
|
4
58
|
|
|
5
59
|
### Minor Changes
|
|
@@ -26,6 +26,8 @@ import {
|
|
|
26
26
|
LIVENESS_FILTER_VALUES,
|
|
27
27
|
LIVENESS_STATUSES,
|
|
28
28
|
ORGANIZATION_METRICS_INCLUDE_FIELDS,
|
|
29
|
+
PROPOSAL_REJECT_REASONS,
|
|
30
|
+
PROPOSAL_WATCHLIST_REASONS,
|
|
29
31
|
REPORT_REVIEW_DECISIONS,
|
|
30
32
|
REPORT_STATUSES,
|
|
31
33
|
TAKEDOWN_SORT_KEYS,
|
|
@@ -56,6 +58,8 @@ export {
|
|
|
56
58
|
LIVENESS_FILTER_VALUES,
|
|
57
59
|
LIVENESS_STATUSES,
|
|
58
60
|
ORGANIZATION_METRICS_INCLUDE_FIELDS,
|
|
61
|
+
PROPOSAL_REJECT_REASONS,
|
|
62
|
+
PROPOSAL_WATCHLIST_REASONS,
|
|
59
63
|
REPORT_REVIEW_DECISIONS,
|
|
60
64
|
REPORT_STATUSES,
|
|
61
65
|
TAKEDOWN_SORT_KEYS,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
installCompletions,
|
|
3
3
|
uninstallCompletions
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-P5DJRDMV.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";
|
|
@@ -22,6 +22,7 @@ _chainpatrol() {
|
|
|
22
22
|
'detections:Validate, run, list, update detection configs and results'
|
|
23
23
|
'metrics:Query organization metrics'
|
|
24
24
|
'reports:Create, list, search reports from terminal'
|
|
25
|
+
'proposals:Review proposals waiting on your organization'
|
|
25
26
|
'queues:Snapshot operations queues'
|
|
26
27
|
'takedowns:List individual takedown records'
|
|
27
28
|
'threats:List confirmed threats blocked by ChainPatrol'
|
|
@@ -106,6 +107,11 @@ _chainpatrol() {
|
|
|
106
107
|
)
|
|
107
108
|
_describe 'subcommand' reports_subcommands
|
|
108
109
|
;;
|
|
110
|
+
proposals)
|
|
111
|
+
local -a proposals_subcommands
|
|
112
|
+
proposals_subcommands=('review:Approve, reject, or watchlist a proposal')
|
|
113
|
+
_describe 'subcommand' proposals_subcommands
|
|
114
|
+
;;
|
|
109
115
|
queues)
|
|
110
116
|
local -a queues_subcommands
|
|
111
117
|
queues_subcommands=('snapshot:Show queue snapshot')
|
|
@@ -154,7 +160,7 @@ var BASH_COMPLETION = `_chainpatrol() {
|
|
|
154
160
|
COMPREPLY=()
|
|
155
161
|
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
156
162
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
157
|
-
commands="login logout whoami asset configs detections metrics reports queues takedowns threats orgs presets setup uninstall completions help"
|
|
163
|
+
commands="login logout whoami asset configs detections metrics reports proposals queues takedowns threats orgs presets setup uninstall completions help"
|
|
158
164
|
|
|
159
165
|
case "\${prev}" in
|
|
160
166
|
chainpatrol)
|
|
@@ -181,6 +187,10 @@ var BASH_COMPLETION = `_chainpatrol() {
|
|
|
181
187
|
COMPREPLY=( $(compgen -W "create list search" -- "\${cur}") )
|
|
182
188
|
return 0
|
|
183
189
|
;;
|
|
190
|
+
proposals)
|
|
191
|
+
COMPREPLY=( $(compgen -W "review" -- "\${cur}") )
|
|
192
|
+
return 0
|
|
193
|
+
;;
|
|
184
194
|
queues)
|
|
185
195
|
COMPREPLY=( $(compgen -W "snapshot" -- "\${cur}") )
|
|
186
196
|
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-55B34HJK.js";
|
|
17
|
+
import "./chunk-P5DJRDMV.js";
|
|
18
18
|
import {
|
|
19
19
|
DateTime
|
|
20
20
|
} from "./chunk-TFCNKBRC.js";
|
|
@@ -426,6 +426,39 @@ var HELP = {
|
|
|
426
426
|
"chainpatrol reports list --org acme --needs-customer-review"
|
|
427
427
|
]
|
|
428
428
|
},
|
|
429
|
+
proposals: {
|
|
430
|
+
description: "Review the proposals waiting on your organization.",
|
|
431
|
+
usage: "chainpatrol proposals review <proposalId> [options]",
|
|
432
|
+
examples: [
|
|
433
|
+
"chainpatrol reports list --org acme --needs-customer-review",
|
|
434
|
+
'chainpatrol proposals review 4321 --asset-id 987 --decision approve --label "General Phishing"',
|
|
435
|
+
"chainpatrol proposals review 4321 --asset-id 987 --decision reject --reject-reason irrelevant"
|
|
436
|
+
]
|
|
437
|
+
},
|
|
438
|
+
"proposals review": {
|
|
439
|
+
description: "Approve, reject, or watchlist a proposal that is waiting on your organization's review \u2014 the same queue the Review page shows and `reports list --needs-customer-review` returns. Find the proposal and asset IDs in that listing's `proposals[].id` and `proposals[].asset.id`. A review cannot be undone over the API, so the command asks for confirmation unless you pass --yes, and it is capped at 10 reviews per organization per day across all of the org's API credentials.",
|
|
440
|
+
usage: "chainpatrol proposals review <proposalId> --asset-id <id> --decision <approve|reject|watchlist> [options]",
|
|
441
|
+
options: [
|
|
442
|
+
"--proposal-id <id> Proposal to review (alternative to the positional arg)",
|
|
443
|
+
"--asset-id <id> Asset the proposal targets; must match, or the API returns 409",
|
|
444
|
+
"--decision <d> approve|reject|watchlist (watchlist rejects and parks the asset)",
|
|
445
|
+
'--label <text> Block label, required when approving. Quote it: e.g. "General Phishing", "Brand Impersonation", "Employee Impersonation", "Fake Employee", "Targeting Org Users", "C2 Server", "False Positive"',
|
|
446
|
+
"--brand-id <id> Brand the asset impersonates; required for Brand/Employee Impersonation",
|
|
447
|
+
"--reject-reason <r> Required when rejecting: irrelevant|no_malicious_activity|insufficient_evidence|not_targeting_org|decayed",
|
|
448
|
+
"--watchlist-reason <r> Required when watchlisting: DEAD|PARKING|NOT_ENOUGH_EVIDENCE|OTHER",
|
|
449
|
+
"--note <text> Free-text detail; required when --watchlist-reason OTHER",
|
|
450
|
+
"--org <slug> Organization slug (optional for org-scoped API keys)",
|
|
451
|
+
"--yes Skip the confirmation prompt (required when not on a TTY)",
|
|
452
|
+
"--dry-run Preview the payload without submitting"
|
|
453
|
+
],
|
|
454
|
+
examples: [
|
|
455
|
+
'chainpatrol proposals review 4321 --asset-id 987 --decision approve --label "General Phishing" --yes',
|
|
456
|
+
'chainpatrol proposals review 4321 --asset-id 987 --decision approve --label "Brand Impersonation" --brand-id 12 --yes',
|
|
457
|
+
"chainpatrol proposals review 4321 --asset-id 987 --decision reject --reject-reason no_malicious_activity --yes",
|
|
458
|
+
'chainpatrol proposals review 4321 --asset-id 987 --decision watchlist --watchlist-reason OTHER --note "Registrar suspended it" --yes',
|
|
459
|
+
"chainpatrol proposals review 4321 --asset-id 987 --decision reject --reject-reason decayed --dry-run"
|
|
460
|
+
]
|
|
461
|
+
},
|
|
429
462
|
queues: {
|
|
430
463
|
description: "Snapshot operations review/takedown queues.",
|
|
431
464
|
usage: "chainpatrol queues snapshot [--org <slug>|--all]",
|
|
@@ -681,6 +714,7 @@ function getTopLevelHelp() {
|
|
|
681
714
|
" healthchecks List and run organization healthchecks",
|
|
682
715
|
" metrics Query organization metrics and breakdowns",
|
|
683
716
|
" reports Create, list, and search reports from terminal",
|
|
717
|
+
" proposals Review proposals waiting on your organization",
|
|
684
718
|
" queues Snapshot operations review/takedown queues",
|
|
685
719
|
" takedowns List individual takedown records",
|
|
686
720
|
" threats List confirmed threats blocked by ChainPatrol",
|
|
@@ -854,6 +888,7 @@ var COMMANDS = [
|
|
|
854
888
|
"healthchecks",
|
|
855
889
|
"metrics",
|
|
856
890
|
"reports",
|
|
891
|
+
"proposals",
|
|
857
892
|
"queues",
|
|
858
893
|
"takedowns",
|
|
859
894
|
"threats",
|
|
@@ -936,6 +971,15 @@ ${getTopLevelHelp()}
|
|
|
936
971
|
search: { type: "string" },
|
|
937
972
|
reportedByCustomer: { type: "boolean" },
|
|
938
973
|
needsCustomerReview: { type: "boolean" },
|
|
974
|
+
proposalId: { type: "number" },
|
|
975
|
+
assetId: { type: "number" },
|
|
976
|
+
decision: { type: "string" },
|
|
977
|
+
label: { type: "string" },
|
|
978
|
+
brandId: { type: "number" },
|
|
979
|
+
rejectReason: { type: "string" },
|
|
980
|
+
watchlistReason: { type: "string" },
|
|
981
|
+
note: { type: "string" },
|
|
982
|
+
yes: { type: "boolean", default: false },
|
|
939
983
|
attachmentUrl: { type: "string" },
|
|
940
984
|
contactInfo: { type: "string" },
|
|
941
985
|
externalSubmissionLink: { type: "string" },
|
|
@@ -1115,12 +1159,12 @@ function parseAttachmentUrls() {
|
|
|
1115
1159
|
}
|
|
1116
1160
|
async function handleConfigsList(org) {
|
|
1117
1161
|
if (jsonMode) {
|
|
1118
|
-
const { listConfigsJson } = await import("./list-json-
|
|
1162
|
+
const { listConfigsJson } = await import("./list-json-ZR3XOQPJ.js");
|
|
1119
1163
|
await listConfigsJson({ org });
|
|
1120
1164
|
return;
|
|
1121
1165
|
}
|
|
1122
1166
|
const { render } = await import("ink");
|
|
1123
|
-
const { default: ConfigsList } = await import("./list-
|
|
1167
|
+
const { default: ConfigsList } = await import("./list-6CAKS43I.js");
|
|
1124
1168
|
const { default: React } = await import("react");
|
|
1125
1169
|
render(React.createElement(ConfigsList, { org }));
|
|
1126
1170
|
}
|
|
@@ -1159,7 +1203,7 @@ async function main() {
|
|
|
1159
1203
|
return;
|
|
1160
1204
|
}
|
|
1161
1205
|
if (cliContext.dryRun) {
|
|
1162
|
-
const dryRunSupported = command === "reports" && subcommand === "create" || command === "detections" && subcommand === "run" || command === "detections" && subcommand === "configs" && action === "run" || command === "detections" && subcommand === "configs" && action === "update";
|
|
1206
|
+
const dryRunSupported = command === "reports" && subcommand === "create" || command === "proposals" && subcommand === "review" || command === "detections" && subcommand === "run" || command === "detections" && subcommand === "configs" && action === "run" || command === "detections" && subcommand === "configs" && action === "update";
|
|
1163
1207
|
if (!dryRunSupported) {
|
|
1164
1208
|
throw new CliExitError(
|
|
1165
1209
|
"--dry-run is only supported for mutation commands.",
|
|
@@ -1220,7 +1264,7 @@ async function main() {
|
|
|
1220
1264
|
const positionals = cli.input.slice(2);
|
|
1221
1265
|
const flagAssets = parseAssetInputs();
|
|
1222
1266
|
const contents = [...positionals, ...flagAssets];
|
|
1223
|
-
const { runAssetCheck } = await import("./check-
|
|
1267
|
+
const { runAssetCheck } = await import("./check-5LJI7N5I.js");
|
|
1224
1268
|
await runAssetCheck({
|
|
1225
1269
|
contents,
|
|
1226
1270
|
json: jsonMode,
|
|
@@ -1230,7 +1274,7 @@ async function main() {
|
|
|
1230
1274
|
break;
|
|
1231
1275
|
}
|
|
1232
1276
|
if (subcommand === "list") {
|
|
1233
|
-
const { runAssetList } = await import("./list-
|
|
1277
|
+
const { runAssetList } = await import("./list-UXYGISAK.js");
|
|
1234
1278
|
await runAssetList({
|
|
1235
1279
|
type: cli.flags.type ?? cli.flags.assetType,
|
|
1236
1280
|
status: cli.flags.assetStatus ?? cli.flags.status,
|
|
@@ -1255,7 +1299,7 @@ async function main() {
|
|
|
1255
1299
|
}
|
|
1256
1300
|
return n;
|
|
1257
1301
|
});
|
|
1258
|
-
const { runAssetSearch } = await import("./search-
|
|
1302
|
+
const { runAssetSearch } = await import("./search-W5QM2IOC.js");
|
|
1259
1303
|
await runAssetSearch({
|
|
1260
1304
|
contents: contents.length > 0 ? contents : void 0,
|
|
1261
1305
|
ids: ids.length > 0 ? ids : void 0,
|
|
@@ -1280,7 +1324,7 @@ async function main() {
|
|
|
1280
1324
|
case "detections": {
|
|
1281
1325
|
const org = await resolveOrg();
|
|
1282
1326
|
if (subcommand === "healthcheck") {
|
|
1283
|
-
const { runDetectionsHealthcheck } = await import("./healthcheck-
|
|
1327
|
+
const { runDetectionsHealthcheck } = await import("./healthcheck-34LQTSVZ.js");
|
|
1284
1328
|
await runDetectionsHealthcheck({
|
|
1285
1329
|
org,
|
|
1286
1330
|
source: cli.flags.source,
|
|
@@ -1295,7 +1339,7 @@ async function main() {
|
|
|
1295
1339
|
break;
|
|
1296
1340
|
}
|
|
1297
1341
|
if (subcommand === "validate") {
|
|
1298
|
-
const { runDetectionsValidate } = await import("./validate-
|
|
1342
|
+
const { runDetectionsValidate } = await import("./validate-WB5367QN.js");
|
|
1299
1343
|
await runDetectionsValidate({
|
|
1300
1344
|
org,
|
|
1301
1345
|
source: cli.flags.source,
|
|
@@ -1310,7 +1354,7 @@ async function main() {
|
|
|
1310
1354
|
break;
|
|
1311
1355
|
}
|
|
1312
1356
|
if (subcommand === "drift") {
|
|
1313
|
-
const { runDetectionsDrift } = await import("./drift-
|
|
1357
|
+
const { runDetectionsDrift } = await import("./drift-BGWNH6QE.js");
|
|
1314
1358
|
await runDetectionsDrift({
|
|
1315
1359
|
org,
|
|
1316
1360
|
source: cli.flags.source,
|
|
@@ -1324,7 +1368,7 @@ async function main() {
|
|
|
1324
1368
|
break;
|
|
1325
1369
|
}
|
|
1326
1370
|
if (subcommand === "run") {
|
|
1327
|
-
const { runDetectionsRun } = await import("./run-
|
|
1371
|
+
const { runDetectionsRun } = await import("./run-LZZOV4HG.js");
|
|
1328
1372
|
await runDetectionsRun({
|
|
1329
1373
|
org,
|
|
1330
1374
|
configId: cli.flags.configId,
|
|
@@ -1338,7 +1382,7 @@ async function main() {
|
|
|
1338
1382
|
break;
|
|
1339
1383
|
}
|
|
1340
1384
|
if (subcommand === "list") {
|
|
1341
|
-
const { runDetectionsList } = await import("./list-
|
|
1385
|
+
const { runDetectionsList } = await import("./list-DJFIKDY6.js");
|
|
1342
1386
|
await runDetectionsList({
|
|
1343
1387
|
org,
|
|
1344
1388
|
cursor: cli.flags.cursor,
|
|
@@ -1367,7 +1411,7 @@ async function main() {
|
|
|
1367
1411
|
break;
|
|
1368
1412
|
}
|
|
1369
1413
|
if (action === "run") {
|
|
1370
|
-
const { runDetectionsRun } = await import("./run-
|
|
1414
|
+
const { runDetectionsRun } = await import("./run-LZZOV4HG.js");
|
|
1371
1415
|
await runDetectionsRun({
|
|
1372
1416
|
org,
|
|
1373
1417
|
configId: cli.flags.configId,
|
|
@@ -1385,7 +1429,7 @@ async function main() {
|
|
|
1385
1429
|
throw new Error("detections configs update requires --config-id");
|
|
1386
1430
|
}
|
|
1387
1431
|
const configPatch = getConfigPatchFromSetFlags();
|
|
1388
|
-
const { runDetectionsConfigsUpdate } = await import("./configs-update-
|
|
1432
|
+
const { runDetectionsConfigsUpdate } = await import("./configs-update-N7ZIWWKQ.js");
|
|
1389
1433
|
await runDetectionsConfigsUpdate({
|
|
1390
1434
|
org,
|
|
1391
1435
|
configId: cli.flags.configId,
|
|
@@ -1421,7 +1465,7 @@ async function main() {
|
|
|
1421
1465
|
const slugs = parseCsvList(cli.flags.slugs);
|
|
1422
1466
|
const allMyOrgs = cli.flags.allMyOrgs;
|
|
1423
1467
|
const org2 = slugs && slugs.length > 0 || allMyOrgs ? void 0 : await tryResolveOrg();
|
|
1424
|
-
const { runMetricsOrganization } = await import("./organization-
|
|
1468
|
+
const { runMetricsOrganization } = await import("./organization-YAID36GZ.js");
|
|
1425
1469
|
await runMetricsOrganization({
|
|
1426
1470
|
org: org2,
|
|
1427
1471
|
slugs,
|
|
@@ -1440,7 +1484,7 @@ async function main() {
|
|
|
1440
1484
|
}
|
|
1441
1485
|
const org = await resolveOrg();
|
|
1442
1486
|
if (subcommand === "summary") {
|
|
1443
|
-
const { runMetricsSummary } = await import("./summary-
|
|
1487
|
+
const { runMetricsSummary } = await import("./summary-XA3PVJIW.js");
|
|
1444
1488
|
await runMetricsSummary({
|
|
1445
1489
|
org,
|
|
1446
1490
|
from: cli.flags.from,
|
|
@@ -1453,7 +1497,7 @@ async function main() {
|
|
|
1453
1497
|
break;
|
|
1454
1498
|
}
|
|
1455
1499
|
if (subcommand === "found") {
|
|
1456
|
-
const { runMetricsFound } = await import("./found-
|
|
1500
|
+
const { runMetricsFound } = await import("./found-QOARB5G2.js");
|
|
1457
1501
|
await runMetricsFound({
|
|
1458
1502
|
org,
|
|
1459
1503
|
from: cli.flags.from,
|
|
@@ -1470,7 +1514,7 @@ async function main() {
|
|
|
1470
1514
|
if (!by || !["day", "type", "brand"].includes(by)) {
|
|
1471
1515
|
throw new Error("metrics breakdown requires --by <day|type|brand>");
|
|
1472
1516
|
}
|
|
1473
|
-
const { runMetricsBreakdown } = await import("./breakdown-
|
|
1517
|
+
const { runMetricsBreakdown } = await import("./breakdown-2HAZ2MX5.js");
|
|
1474
1518
|
await runMetricsBreakdown({
|
|
1475
1519
|
org,
|
|
1476
1520
|
by,
|
|
@@ -1491,7 +1535,7 @@ async function main() {
|
|
|
1491
1535
|
case "reports": {
|
|
1492
1536
|
if (subcommand === "list") {
|
|
1493
1537
|
const org = await resolveOrg();
|
|
1494
|
-
const { runReportsList } = await import("./list-
|
|
1538
|
+
const { runReportsList } = await import("./list-AG6LWNMN.js");
|
|
1495
1539
|
await runReportsList({
|
|
1496
1540
|
org,
|
|
1497
1541
|
limit: cli.flags.limit,
|
|
@@ -1522,7 +1566,7 @@ async function main() {
|
|
|
1522
1566
|
}
|
|
1523
1567
|
if (subcommand === "create") {
|
|
1524
1568
|
const org = await tryResolveOrg();
|
|
1525
|
-
const { runReportsCreate } = await import("./create-
|
|
1569
|
+
const { runReportsCreate } = await import("./create-4ZJ7DXHX.js");
|
|
1526
1570
|
await runReportsCreate({
|
|
1527
1571
|
org,
|
|
1528
1572
|
title: cli.flags.title,
|
|
@@ -1544,7 +1588,7 @@ async function main() {
|
|
|
1544
1588
|
const positionals = cli.input.slice(2);
|
|
1545
1589
|
const flagAssets = parseAssetInputs();
|
|
1546
1590
|
const assetContents = [...positionals, ...flagAssets];
|
|
1547
|
-
const { runReportsSearch } = await import("./search-
|
|
1591
|
+
const { runReportsSearch } = await import("./search-OGGKNPUJ.js");
|
|
1548
1592
|
await runReportsSearch({
|
|
1549
1593
|
assetContents,
|
|
1550
1594
|
org,
|
|
@@ -1560,9 +1604,38 @@ async function main() {
|
|
|
1560
1604
|
subcommand ? `Unknown subcommand: reports ${subcommand}${hint ? `. Did you mean "reports ${hint}"?` : ""}` : "Usage: chainpatrol reports <create|list|search>"
|
|
1561
1605
|
);
|
|
1562
1606
|
}
|
|
1607
|
+
case "proposals": {
|
|
1608
|
+
if (subcommand === "review") {
|
|
1609
|
+
const org = await tryResolveOrg();
|
|
1610
|
+
const positionalProposalId = cli.input[2];
|
|
1611
|
+
const { runProposalsReview } = await import("./review-7A7WV73T.js");
|
|
1612
|
+
await runProposalsReview({
|
|
1613
|
+
org,
|
|
1614
|
+
proposalId: cli.flags.proposalId ?? (positionalProposalId ? Number(positionalProposalId) : void 0),
|
|
1615
|
+
assetId: cli.flags.assetId,
|
|
1616
|
+
decision: cli.flags.decision,
|
|
1617
|
+
label: cli.flags.label,
|
|
1618
|
+
brandId: cli.flags.brandId,
|
|
1619
|
+
rejectReason: cli.flags.rejectReason,
|
|
1620
|
+
watchlistReason: cli.flags.watchlistReason,
|
|
1621
|
+
note: cli.flags.note,
|
|
1622
|
+
yes: cli.flags.yes,
|
|
1623
|
+
noInput: cli.flags.noInput,
|
|
1624
|
+
json: jsonMode,
|
|
1625
|
+
outputFormat: cliContext.outputFormat,
|
|
1626
|
+
explain: cliContext.explain,
|
|
1627
|
+
dryRun: cliContext.dryRun
|
|
1628
|
+
});
|
|
1629
|
+
break;
|
|
1630
|
+
}
|
|
1631
|
+
const hint = subcommand ? suggest(subcommand, ["review"]) : null;
|
|
1632
|
+
throw new Error(
|
|
1633
|
+
subcommand ? `Unknown subcommand: proposals ${subcommand}${hint ? `. Did you mean "proposals ${hint}"?` : ""}` : "Usage: chainpatrol proposals review <proposalId> --asset-id <id> --decision <approve|reject|watchlist>"
|
|
1634
|
+
);
|
|
1635
|
+
}
|
|
1563
1636
|
case "threats": {
|
|
1564
1637
|
if (subcommand === "list") {
|
|
1565
|
-
const { runThreatsList } = await import("./list-
|
|
1638
|
+
const { runThreatsList } = await import("./list-UPY6PHY4.js");
|
|
1566
1639
|
await runThreatsList({
|
|
1567
1640
|
query: cli.flags.query,
|
|
1568
1641
|
from: cli.flags.from,
|
|
@@ -1585,7 +1658,7 @@ async function main() {
|
|
|
1585
1658
|
case "takedowns": {
|
|
1586
1659
|
if (subcommand === "list") {
|
|
1587
1660
|
const org = await tryResolveOrg();
|
|
1588
|
-
const { runTakedownsList } = await import("./list-
|
|
1661
|
+
const { runTakedownsList } = await import("./list-PAJBOVN2.js");
|
|
1589
1662
|
await runTakedownsList({
|
|
1590
1663
|
org,
|
|
1591
1664
|
query: cli.flags.query,
|
|
@@ -1616,7 +1689,7 @@ async function main() {
|
|
|
1616
1689
|
}
|
|
1617
1690
|
case "queues": {
|
|
1618
1691
|
if (subcommand === "snapshot") {
|
|
1619
|
-
const { runQueuesSnapshot } = await import("./snapshot-
|
|
1692
|
+
const { runQueuesSnapshot } = await import("./snapshot-TLSOPK7C.js");
|
|
1620
1693
|
await runQueuesSnapshot({
|
|
1621
1694
|
org: cli.flags.org,
|
|
1622
1695
|
all: cli.flags.all,
|
|
@@ -1634,7 +1707,7 @@ async function main() {
|
|
|
1634
1707
|
}
|
|
1635
1708
|
case "orgs": {
|
|
1636
1709
|
if (subcommand === "list") {
|
|
1637
|
-
const { runOrgsList } = await import("./list-
|
|
1710
|
+
const { runOrgsList } = await import("./list-MHOHKFXT.js");
|
|
1638
1711
|
await runOrgsList({
|
|
1639
1712
|
query: cli.flags.query,
|
|
1640
1713
|
subscriptionStatus: cli.flags.subscriptionStatus,
|
|
@@ -1656,7 +1729,7 @@ async function main() {
|
|
|
1656
1729
|
if (subcommand === "get") {
|
|
1657
1730
|
const positionalSlug = action;
|
|
1658
1731
|
const slug = positionalSlug ?? await tryResolveOrg();
|
|
1659
|
-
const { runOrgsGet } = await import("./get-
|
|
1732
|
+
const { runOrgsGet } = await import("./get-ON5JOCV7.js");
|
|
1660
1733
|
await runOrgsGet({
|
|
1661
1734
|
slug,
|
|
1662
1735
|
json: jsonMode,
|
|
@@ -1666,7 +1739,7 @@ async function main() {
|
|
|
1666
1739
|
}
|
|
1667
1740
|
if (subcommand === "assets") {
|
|
1668
1741
|
if (action === "list") {
|
|
1669
|
-
const { runOrgsAssetsList } = await import("./list-
|
|
1742
|
+
const { runOrgsAssetsList } = await import("./list-H6QWWXOU.js");
|
|
1670
1743
|
await runOrgsAssetsList({
|
|
1671
1744
|
type: cli.flags.type ?? cli.flags.assetType,
|
|
1672
1745
|
groupId: cli.flags.groupId,
|
|
@@ -1682,7 +1755,7 @@ async function main() {
|
|
|
1682
1755
|
}
|
|
1683
1756
|
if (subcommand === "asset-groups") {
|
|
1684
1757
|
if (action === "list") {
|
|
1685
|
-
const { runOrgsAssetGroupsList } = await import("./list-
|
|
1758
|
+
const { runOrgsAssetGroupsList } = await import("./list-SJFJT4V3.js");
|
|
1686
1759
|
await runOrgsAssetGroupsList({
|
|
1687
1760
|
json: jsonMode,
|
|
1688
1761
|
outputFormat: cliContext.outputFormat
|
|
@@ -1699,12 +1772,12 @@ async function main() {
|
|
|
1699
1772
|
case "brands": {
|
|
1700
1773
|
if (subcommand === "list") {
|
|
1701
1774
|
const rawType = cli.flags.type?.toUpperCase();
|
|
1702
|
-
if (rawType && rawType !== "ORGANIZATION" && rawType !== "INDIVIDUAL") {
|
|
1775
|
+
if (rawType && rawType !== "ORGANIZATION" && rawType !== "INDIVIDUAL" && rawType !== "PRODUCT") {
|
|
1703
1776
|
throw new Error(
|
|
1704
|
-
"brands list --type must be ORGANIZATION or
|
|
1777
|
+
"brands list --type must be ORGANIZATION, INDIVIDUAL, or PRODUCT (case-insensitive)."
|
|
1705
1778
|
);
|
|
1706
1779
|
}
|
|
1707
|
-
const { runBrandsList } = await import("./list-
|
|
1780
|
+
const { runBrandsList } = await import("./list-IVFZGLKN.js");
|
|
1708
1781
|
await runBrandsList({
|
|
1709
1782
|
type: rawType,
|
|
1710
1783
|
json: jsonMode,
|
|
@@ -1714,11 +1787,11 @@ async function main() {
|
|
|
1714
1787
|
}
|
|
1715
1788
|
const hint = subcommand ? suggest(subcommand, ["list"]) : null;
|
|
1716
1789
|
throw new Error(
|
|
1717
|
-
subcommand ? `Unknown subcommand: brands ${subcommand}${hint ? `. Did you mean "brands ${hint}"?` : ""}` : "Usage: chainpatrol brands list [--type ORGANIZATION|INDIVIDUAL]"
|
|
1790
|
+
subcommand ? `Unknown subcommand: brands ${subcommand}${hint ? `. Did you mean "brands ${hint}"?` : ""}` : "Usage: chainpatrol brands list [--type ORGANIZATION|INDIVIDUAL|PRODUCT]"
|
|
1718
1791
|
);
|
|
1719
1792
|
}
|
|
1720
1793
|
case "whoami": {
|
|
1721
|
-
const { runWhoami } = await import("./whoami-
|
|
1794
|
+
const { runWhoami } = await import("./whoami-YE3V6I65.js");
|
|
1722
1795
|
await runWhoami({
|
|
1723
1796
|
json: jsonMode,
|
|
1724
1797
|
outputFormat: cliContext.outputFormat
|
|
@@ -1727,7 +1800,7 @@ async function main() {
|
|
|
1727
1800
|
}
|
|
1728
1801
|
case "healthchecks": {
|
|
1729
1802
|
if (subcommand === "list") {
|
|
1730
|
-
const { runHealthchecksList } = await import("./list-
|
|
1803
|
+
const { runHealthchecksList } = await import("./list-W6CF25ZS.js");
|
|
1731
1804
|
await runHealthchecksList({
|
|
1732
1805
|
json: jsonMode,
|
|
1733
1806
|
outputFormat: cliContext.outputFormat
|
|
@@ -1741,7 +1814,7 @@ async function main() {
|
|
|
1741
1814
|
thresholds.minResults = cli.flags.minResults;
|
|
1742
1815
|
if (cli.flags.lookbackHours !== void 0)
|
|
1743
1816
|
thresholds.lookbackHours = cli.flags.lookbackHours;
|
|
1744
|
-
const { runHealthchecksRun } = await import("./run-
|
|
1817
|
+
const { runHealthchecksRun } = await import("./run-AIYPHWFJ.js");
|
|
1745
1818
|
await runHealthchecksRun({
|
|
1746
1819
|
org,
|
|
1747
1820
|
id: action,
|
|
@@ -1759,7 +1832,7 @@ async function main() {
|
|
|
1759
1832
|
}
|
|
1760
1833
|
case "presets": {
|
|
1761
1834
|
if (subcommand === "list") {
|
|
1762
|
-
const { runPresetsList } = await import("./list-
|
|
1835
|
+
const { runPresetsList } = await import("./list-JG5ARDTR.js");
|
|
1763
1836
|
await runPresetsList({ outputFormat: cliContext.outputFormat });
|
|
1764
1837
|
break;
|
|
1765
1838
|
}
|
|
@@ -1770,7 +1843,7 @@ async function main() {
|
|
|
1770
1843
|
);
|
|
1771
1844
|
}
|
|
1772
1845
|
const org = await resolveOrg();
|
|
1773
|
-
const { runPresetsRun } = await import("./run-
|
|
1846
|
+
const { runPresetsRun } = await import("./run-YP4O5VXZ.js");
|
|
1774
1847
|
await runPresetsRun({
|
|
1775
1848
|
presetId: action,
|
|
1776
1849
|
org,
|
|
@@ -1787,17 +1860,17 @@ async function main() {
|
|
|
1787
1860
|
case "setup":
|
|
1788
1861
|
case "install":
|
|
1789
1862
|
case "i": {
|
|
1790
|
-
const { setupSkill } = await import("./setup-skill-
|
|
1863
|
+
const { setupSkill } = await import("./setup-skill-2KCAXU2U.js");
|
|
1791
1864
|
setupSkill({ json: jsonMode, cloud: cli.flags.cloud });
|
|
1792
1865
|
break;
|
|
1793
1866
|
}
|
|
1794
1867
|
case "uninstall": {
|
|
1795
|
-
const { uninstallSkill } = await import("./setup-skill-
|
|
1868
|
+
const { uninstallSkill } = await import("./setup-skill-2KCAXU2U.js");
|
|
1796
1869
|
uninstallSkill({ json: jsonMode });
|
|
1797
1870
|
break;
|
|
1798
1871
|
}
|
|
1799
1872
|
case "completions": {
|
|
1800
|
-
const { printCompletions } = await import("./completions-
|
|
1873
|
+
const { printCompletions } = await import("./completions-FEUVEC5N.js");
|
|
1801
1874
|
printCompletions(subcommand);
|
|
1802
1875
|
break;
|
|
1803
1876
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRESETS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FPCN4SNK.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-322AVMUH.js";
|
|
10
10
|
import "./chunk-TFCNKBRC.js";
|
|
11
11
|
import "./chunk-EGWK6SRQ.js";
|
|
12
12
|
import "./chunk-U73SABXK.js";
|
|
@@ -0,0 +1,270 @@
|
|
|
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
|
+
PROPOSAL_REJECT_REASONS,
|
|
11
|
+
PROPOSAL_WATCHLIST_REASONS,
|
|
12
|
+
createApiClient
|
|
13
|
+
} from "./chunk-322AVMUH.js";
|
|
14
|
+
import "./chunk-EGWK6SRQ.js";
|
|
15
|
+
import "./chunk-U73SABXK.js";
|
|
16
|
+
|
|
17
|
+
// src/commands/proposals/review.ts
|
|
18
|
+
var BRAND_REQUIRED_LABELS = ["brand impersonation", "employee impersonation"];
|
|
19
|
+
var DECISIONS = ["APPROVE", "REJECT", "WATCHLIST"];
|
|
20
|
+
function parsePositiveId(value, flag) {
|
|
21
|
+
if (value === void 0) {
|
|
22
|
+
throw new CliExitError(`proposals review requires ${flag} <id>.`, ExitCode.USAGE);
|
|
23
|
+
}
|
|
24
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
25
|
+
throw new CliExitError(`${flag} must be a positive integer.`, ExitCode.USAGE);
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
function parseDecision(value) {
|
|
30
|
+
if (!value) {
|
|
31
|
+
throw new CliExitError(
|
|
32
|
+
"proposals review requires --decision <approve|reject|watchlist>.",
|
|
33
|
+
ExitCode.USAGE
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const normalized = value.trim().toUpperCase();
|
|
37
|
+
if (!DECISIONS.includes(normalized)) {
|
|
38
|
+
throw new CliExitError(
|
|
39
|
+
`Invalid --decision '${value}'. Valid: approve, reject, watchlist.`,
|
|
40
|
+
ExitCode.USAGE
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
function parseRejectReason(value) {
|
|
46
|
+
if (!value) {
|
|
47
|
+
throw new CliExitError(
|
|
48
|
+
`--reject-reason is required when --decision reject. Valid: ${PROPOSAL_REJECT_REASONS.join(
|
|
49
|
+
", "
|
|
50
|
+
)}`,
|
|
51
|
+
ExitCode.USAGE
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const normalized = value.trim().toLowerCase();
|
|
55
|
+
if (!PROPOSAL_REJECT_REASONS.includes(normalized)) {
|
|
56
|
+
throw new CliExitError(
|
|
57
|
+
`Invalid --reject-reason '${value}'. Valid: ${PROPOSAL_REJECT_REASONS.join(", ")}`,
|
|
58
|
+
ExitCode.USAGE
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return normalized;
|
|
62
|
+
}
|
|
63
|
+
function parseWatchlistReason(value) {
|
|
64
|
+
if (!value) {
|
|
65
|
+
throw new CliExitError(
|
|
66
|
+
`--watchlist-reason is required when --decision watchlist. Valid: ${PROPOSAL_WATCHLIST_REASONS.join(
|
|
67
|
+
", "
|
|
68
|
+
)}`,
|
|
69
|
+
ExitCode.USAGE
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
const normalized = value.trim().toUpperCase();
|
|
73
|
+
if (!PROPOSAL_WATCHLIST_REASONS.includes(normalized)) {
|
|
74
|
+
throw new CliExitError(
|
|
75
|
+
`Invalid --watchlist-reason '${value}'. Valid: ${PROPOSAL_WATCHLIST_REASONS.join(
|
|
76
|
+
", "
|
|
77
|
+
)}`,
|
|
78
|
+
ExitCode.USAGE
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return normalized;
|
|
82
|
+
}
|
|
83
|
+
function buildReviewInput(options) {
|
|
84
|
+
const proposalId = parsePositiveId(options.proposalId, "--proposal-id");
|
|
85
|
+
const assetId = parsePositiveId(options.assetId, "--asset-id");
|
|
86
|
+
const decision = parseDecision(options.decision);
|
|
87
|
+
const input = {
|
|
88
|
+
proposalId,
|
|
89
|
+
assetId,
|
|
90
|
+
slug: options.org,
|
|
91
|
+
decision
|
|
92
|
+
};
|
|
93
|
+
if (decision === "APPROVE") {
|
|
94
|
+
const label = options.label?.trim();
|
|
95
|
+
if (!label) {
|
|
96
|
+
throw new CliExitError(
|
|
97
|
+
"--label is required when --decision approve.",
|
|
98
|
+
ExitCode.USAGE
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (BRAND_REQUIRED_LABELS.includes(label.toLowerCase()) && options.brandId === void 0) {
|
|
102
|
+
throw new CliExitError(
|
|
103
|
+
`--brand-id is required when --label is '${label}'. List your brands with \`chainpatrol brands list\`.`,
|
|
104
|
+
ExitCode.USAGE
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
input.label = label;
|
|
108
|
+
if (options.brandId !== void 0) {
|
|
109
|
+
input.brandId = parsePositiveId(options.brandId, "--brand-id");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (decision === "REJECT") {
|
|
113
|
+
input.rejectReason = parseRejectReason(options.rejectReason);
|
|
114
|
+
}
|
|
115
|
+
if (decision === "WATCHLIST") {
|
|
116
|
+
const watchlistReason = parseWatchlistReason(options.watchlistReason);
|
|
117
|
+
if (watchlistReason === "OTHER" && !options.note?.trim()) {
|
|
118
|
+
throw new CliExitError(
|
|
119
|
+
"--note is required when --watchlist-reason OTHER, since the note becomes the reason.",
|
|
120
|
+
ExitCode.USAGE
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
input.watchlistReason = watchlistReason;
|
|
124
|
+
}
|
|
125
|
+
const note = options.note?.trim();
|
|
126
|
+
if (note) {
|
|
127
|
+
input.note = note;
|
|
128
|
+
}
|
|
129
|
+
return input;
|
|
130
|
+
}
|
|
131
|
+
function describeReview(input) {
|
|
132
|
+
const lines = [
|
|
133
|
+
`Decision: ${input.decision}`,
|
|
134
|
+
`Proposal: #${input.proposalId}`,
|
|
135
|
+
`Asset: #${input.assetId}`,
|
|
136
|
+
`Organization: ${input.slug ?? "from credential context"}`
|
|
137
|
+
];
|
|
138
|
+
if (input.label) {
|
|
139
|
+
lines.push(`Label: ${input.label}`);
|
|
140
|
+
}
|
|
141
|
+
if (input.brandId !== void 0) {
|
|
142
|
+
lines.push(`Brand ID: ${input.brandId}`);
|
|
143
|
+
}
|
|
144
|
+
if (input.rejectReason) {
|
|
145
|
+
lines.push(`Reject reason: ${input.rejectReason}`);
|
|
146
|
+
}
|
|
147
|
+
if (input.watchlistReason) {
|
|
148
|
+
lines.push(`Watchlist reason: ${input.watchlistReason}`);
|
|
149
|
+
}
|
|
150
|
+
if (input.note) {
|
|
151
|
+
lines.push(`Note: ${input.note}`);
|
|
152
|
+
}
|
|
153
|
+
return lines;
|
|
154
|
+
}
|
|
155
|
+
async function promptForConfirmation(summary) {
|
|
156
|
+
const { createInterface } = await import("readline/promises");
|
|
157
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
158
|
+
try {
|
|
159
|
+
const answer = await rl.question(`${summary}
|
|
160
|
+
Type "yes" to continue: `);
|
|
161
|
+
const normalized = answer.trim().toLowerCase();
|
|
162
|
+
return normalized === "y" || normalized === "yes";
|
|
163
|
+
} finally {
|
|
164
|
+
rl.close();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function runProposalsReview(options) {
|
|
168
|
+
const input = buildReviewInput(options);
|
|
169
|
+
const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
|
|
170
|
+
const details = describeReview(input);
|
|
171
|
+
if (options.dryRun) {
|
|
172
|
+
printOutput({
|
|
173
|
+
outputFormat,
|
|
174
|
+
json: {
|
|
175
|
+
dryRun: true,
|
|
176
|
+
mutation: "organization.proposals.review",
|
|
177
|
+
payload: input,
|
|
178
|
+
explanation: options.explain ? "This command records a customer review on the proposal and, for APPROVE, blocks the asset." : void 0
|
|
179
|
+
},
|
|
180
|
+
markdown: ["# Dry Run: Proposal Review", "", ...details.map((d) => `- ${d}`)].join(
|
|
181
|
+
"\n"
|
|
182
|
+
),
|
|
183
|
+
csv: toCsvRows([
|
|
184
|
+
{
|
|
185
|
+
proposalId: input.proposalId,
|
|
186
|
+
assetId: input.assetId,
|
|
187
|
+
decision: input.decision,
|
|
188
|
+
label: input.label ?? "",
|
|
189
|
+
rejectReason: input.rejectReason ?? "",
|
|
190
|
+
watchlistReason: input.watchlistReason ?? ""
|
|
191
|
+
}
|
|
192
|
+
]),
|
|
193
|
+
human: () => {
|
|
194
|
+
console.log("Dry run only. No review was submitted.");
|
|
195
|
+
for (const line of details) {
|
|
196
|
+
console.log(line);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (!options.yes) {
|
|
203
|
+
const summary = [
|
|
204
|
+
"About to submit a review that cannot be undone over the API:",
|
|
205
|
+
...details.map((line) => ` ${line}`)
|
|
206
|
+
].join("\n");
|
|
207
|
+
if (options.confirm) {
|
|
208
|
+
if (!await options.confirm(summary)) {
|
|
209
|
+
console.log("Aborted. No review was submitted.");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
} else if (options.noInput || outputFormat !== "human" || !process.stdin.isTTY) {
|
|
213
|
+
throw new CliExitError(
|
|
214
|
+
"proposals review cannot be undone and needs confirmation. Re-run with --yes, or run it in an interactive terminal.",
|
|
215
|
+
ExitCode.USAGE
|
|
216
|
+
);
|
|
217
|
+
} else if (!await promptForConfirmation(summary)) {
|
|
218
|
+
console.log("Aborted. No review was submitted.");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const client = options.apiClient ?? createApiClient();
|
|
223
|
+
const result = await client.reviewOrganizationProposal(input);
|
|
224
|
+
const row = {
|
|
225
|
+
proposalId: result.proposal.id,
|
|
226
|
+
reviewStatus: result.proposal.reviewStatus,
|
|
227
|
+
assetId: result.proposal.asset.id,
|
|
228
|
+
assetType: result.proposal.asset.type,
|
|
229
|
+
assetContent: result.proposal.asset.content,
|
|
230
|
+
assetStatus: result.proposal.asset.status,
|
|
231
|
+
reviewId: result.review.id,
|
|
232
|
+
decision: result.review.decision,
|
|
233
|
+
label: result.review.label,
|
|
234
|
+
watchlisted: result.watchlisted
|
|
235
|
+
};
|
|
236
|
+
printOutput({
|
|
237
|
+
outputFormat,
|
|
238
|
+
json: {
|
|
239
|
+
...result,
|
|
240
|
+
explanation: options.explain ? "A WATCHLIST request is recorded as a REJECT review with the asset parked for re-evaluation." : void 0
|
|
241
|
+
},
|
|
242
|
+
markdown: [
|
|
243
|
+
"# Proposal Reviewed",
|
|
244
|
+
"",
|
|
245
|
+
`- Proposal: #${row.proposalId} (${row.reviewStatus})`,
|
|
246
|
+
`- Asset: #${row.assetId} ${row.assetType} ${row.assetContent} \u2192 ${row.assetStatus}`,
|
|
247
|
+
`- Review: #${row.reviewId} ${row.decision}${row.label ? ` (${row.label})` : ""}`,
|
|
248
|
+
`- Watchlisted: ${row.watchlisted ? "yes" : "no"}`
|
|
249
|
+
].join("\n"),
|
|
250
|
+
csv: toCsvRows([row]),
|
|
251
|
+
human: () => {
|
|
252
|
+
console.log(`Reviewed proposal #${row.proposalId} as ${row.decision}`);
|
|
253
|
+
console.log(`Asset: ${row.assetType} ${row.assetContent} \u2192 ${row.assetStatus}`);
|
|
254
|
+
console.log(`Review: #${row.reviewId}${row.label ? ` label=${row.label}` : ""}`);
|
|
255
|
+
if (row.watchlisted) {
|
|
256
|
+
console.log("Asset parked on the watchlist for re-evaluation.");
|
|
257
|
+
}
|
|
258
|
+
if (options.explain) {
|
|
259
|
+
console.log(
|
|
260
|
+
"Reviews over the API are limited to 10 per organization per day and are shared across all of the org's API credentials."
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
export {
|
|
267
|
+
buildReviewInput,
|
|
268
|
+
describeReview,
|
|
269
|
+
runProposalsReview
|
|
270
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getPresetDefinition,
|
|
3
3
|
runPreset
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FPCN4SNK.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-322AVMUH.js";
|
|
11
11
|
import "./chunk-TFCNKBRC.js";
|
|
12
12
|
import "./chunk-EGWK6SRQ.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-55B34HJK.js";
|
|
10
|
+
import "./chunk-P5DJRDMV.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": "1.
|
|
5
|
+
"version": "1.6.0",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"homepage": "https://chainpatrol.com/docs/cli",
|
|
8
8
|
"keywords": [
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"lint": "npx oxlint ."
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@chainpatrol/sdk": "1.
|
|
36
|
+
"@chainpatrol/sdk": "1.6.0",
|
|
37
37
|
"ink": "^7.0.1",
|
|
38
38
|
"meow": "^14.1.0",
|
|
39
39
|
"open": "^11.0.0",
|