@chainpatrol/cli 0.18.0 → 0.20.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 +78 -0
- package/dist/{breakdown-ETSF2BZ3.js → breakdown-3LJBT3JQ.js} +1 -1
- package/dist/{check-EMGFDXUO.js → check-G64UQCEL.js} +1 -1
- package/dist/{chunk-HGAQBGGL.js → chunk-2JZAPTY6.js} +1 -1
- package/dist/{chunk-MDDC4F6Q.js → chunk-3LJTWYT6.js} +250 -2
- package/dist/{chunk-NQT77IB7.js → chunk-6VTWWGNV.js} +2 -1
- package/dist/{chunk-DPLUSURP.js → chunk-O7TMOF4W.js} +3 -0
- package/dist/cli.js +81 -40
- package/dist/{completions-6C6KG5DH.js → completions-ILC76OX5.js} +1 -1
- package/dist/{configs-update-EKNLIRA6.js → configs-update-MVX7Q5NW.js} +1 -1
- package/dist/{create-M4PV5VVU.js → create-ANCO4FXB.js} +1 -1
- package/dist/{drift-RQKTGJWB.js → drift-SHPMEJDU.js} +1 -1
- package/dist/{found-LVYBEOS6.js → found-OPL4HWOW.js} +1 -1
- package/dist/{get-YFYSJFBC.js → get-K7VHV5X2.js} +1 -1
- package/dist/{healthcheck-J6BIZEG7.js → healthcheck-QNLFKAXM.js} +1 -1
- package/dist/list-4VKMEKZS.js +64 -0
- package/dist/{list-CNF2FGLN.js → list-AQ7E23T3.js} +1 -1
- package/dist/{list-JCJHIJM4.js → list-FJBWSGTD.js} +2 -2
- package/dist/{list-O36VWTWR.js → list-FVFF4VW3.js} +1 -1
- package/dist/{list-S5Y6GW43.js → list-FWXQUOCP.js} +1 -1
- package/dist/{list-E2JVJNHV.js → list-Q5PRMAN5.js} +1 -1
- package/dist/{list-O62BZKQ5.js → list-QHEFB4KE.js} +1 -1
- package/dist/{list-XUYCZG7S.js → list-QXA6GLCV.js} +1 -1
- package/dist/{list-LKNCICLT.js → list-SWHKM5R6.js} +1 -1
- package/dist/{list-44BQZHJD.js → list-WPPNX7G3.js} +1 -1
- package/dist/{list-AEM2KSO4.js → list-X4Z3CPO7.js} +1 -1
- package/dist/{list-2ENAEZVP.js → list-XSIXAHOG.js} +1 -1
- package/dist/{list-json-ZTS5GXNA.js → list-json-B5L5PXKH.js} +1 -1
- package/dist/{organization-PFPBZXJZ.js → organization-SOMHMGTM.js} +1 -1
- package/dist/{run-R3RR6XQF.js → run-J5TQ2YC2.js} +1 -1
- package/dist/{run-HS2EEOM6.js → run-RV6P2OCD.js} +1 -1
- package/dist/{run-LKPKTH3W.js → run-YAAMSWKK.js} +2 -2
- package/dist/{search-EBXEP7XC.js → search-FQUR75WR.js} +1 -1
- package/dist/{setup-skill-Z6SBI6GC.js → setup-skill-A36WQLMU.js} +2 -2
- package/dist/{snapshot-GNZMYTXT.js → snapshot-2GDJMN3L.js} +1 -1
- package/dist/{summary-POVQC6B7.js → summary-LF53KXRR.js} +1 -1
- package/dist/types-PTMFLYUV.js +271 -0
- package/dist/{validate-QE2F4PYY.js → validate-UYKOMUEI.js} +1 -1
- package/dist/{whoami-24KUJUN4.js → whoami-OYOG63BX.js} +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
1
|
# @chainpatrol/cli
|
|
2
2
|
|
|
3
|
+
## 0.20.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e63d269: Add `chainpatrol asset types` — list every supported `AssetType` enum
|
|
8
|
+
value with its human-readable label and a short description.
|
|
9
|
+
|
|
10
|
+
The mapping (e.g. `ARCHIVE_ORG` → "Archive.org" / Wayback snapshot,
|
|
11
|
+
`PAGE` → "Page" / standalone web page tracked independently of its
|
|
12
|
+
parent URL, `FIVE_HUNDRED_PX` → "500px") ships bundled with the CLI as
|
|
13
|
+
a static map, so the command works without auth or a network round
|
|
14
|
+
trip. Output is available in human, JSON, markdown, and CSV formats —
|
|
15
|
+
useful when an agent or script needs to translate enum ↔ display name,
|
|
16
|
+
validate a user-supplied `--asset-type` value, or enumerate the
|
|
17
|
+
available types before constructing a filter for `asset list`,
|
|
18
|
+
`threats list`, `takedowns list`, `detections list`, `reports list`,
|
|
19
|
+
or `orgs assets list`.
|
|
20
|
+
|
|
21
|
+
The Claude Code skill is updated to point at the new command whenever
|
|
22
|
+
a user asks "what is `ARCHIVE_ORG`?", "what does `PAGE` mean?", or
|
|
23
|
+
"what asset types does ChainPatrol support?". Shell completions and
|
|
24
|
+
top-level/per-command help text are updated accordingly.
|
|
25
|
+
|
|
26
|
+
## 0.19.0
|
|
27
|
+
|
|
28
|
+
### Minor Changes
|
|
29
|
+
|
|
30
|
+
- 477a431: Add proper brand support across metrics and listings so agents can run a
|
|
31
|
+
real "trend search" on an org.
|
|
32
|
+
|
|
33
|
+
## New: list brands for an org
|
|
34
|
+
|
|
35
|
+
`GET /organization/brands` (CLI: `chainpatrol brands list`) returns every
|
|
36
|
+
brand belonging to the caller's org — both parent / product brands
|
|
37
|
+
(`type: ORGANIZATION`) and individual / employee brands (`type:
|
|
38
|
+
INDIVIDUAL`) — in one call. Soft-deleted brands are excluded. Use
|
|
39
|
+
`--type INDIVIDUAL` to filter to employee brands only. The endpoint is
|
|
40
|
+
org-scoped via auth, no extra args.
|
|
41
|
+
|
|
42
|
+
## Fixed: brand filter actually applies on metrics
|
|
43
|
+
|
|
44
|
+
Two server-side bugs the trend-search docs ran into:
|
|
45
|
+
|
|
46
|
+
1. `GET /organization/metrics` with `brandSlug` was filtering the scalar
|
|
47
|
+
metrics (`reports`, `newThreats`, `takedownsFiled`, ...) but **not**
|
|
48
|
+
`blockedByType` / `blockedByDay`, which silently returned org-wide
|
|
49
|
+
totals. A caller drilling into a single brand would see "this brand
|
|
50
|
+
is being attacked on YouTube" when YouTube was really just an
|
|
51
|
+
org-wide trend.
|
|
52
|
+
2. `POST /metrics/breakdown` with `by: "brand"` was ignoring the
|
|
53
|
+
`brandIds` input filter — `getThreatsBlockedPerBrand()` always
|
|
54
|
+
returned every brand. The function now accepts `brandIds` and the
|
|
55
|
+
handler passes the input through.
|
|
56
|
+
|
|
57
|
+
Both are now wired through correctly. `metrics organization
|
|
58
|
+
--brand-slug <slug> --include blockedByType,blockedByDay` returns
|
|
59
|
+
real brand-scoped time series, and `metrics breakdown --by brand
|
|
60
|
+
--brand <id>` actually narrows to the requested brand(s).
|
|
61
|
+
|
|
62
|
+
## Skill: full trend-search guide
|
|
63
|
+
|
|
64
|
+
Documents three trend signals the agent can answer from existing
|
|
65
|
+
commands, with concrete current-vs-baseline recipes:
|
|
66
|
+
|
|
67
|
+
1. Spike in a specific asset type (`metrics breakdown --by type`).
|
|
68
|
+
2. Spike in overall threat volume (`metrics breakdown --by day` +
|
|
69
|
+
`metrics organization`).
|
|
70
|
+
3. Spike on a specific sub-brand (`brands list` → join → `metrics
|
|
71
|
+
breakdown --by brand` → `metrics organization --brand-slug`).
|
|
72
|
+
Employee vs. product distinction now comes straight from the brand
|
|
73
|
+
roster.
|
|
74
|
+
|
|
75
|
+
Server-side procedures + validation schemas live in the private
|
|
76
|
+
`@chainpatrol/external-trpc`, `@chainpatrol/validation`, and
|
|
77
|
+
`@chainpatrol/metrics` packages and ship together with this CLI
|
|
78
|
+
release, but are not listed in the frontmatter because they are
|
|
79
|
+
private.
|
|
80
|
+
|
|
3
81
|
## 0.18.0
|
|
4
82
|
|
|
5
83
|
### Minor Changes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
installCompletions,
|
|
3
3
|
uninstallCompletions
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6VTWWGNV.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";
|
|
@@ -201,12 +201,23 @@ description: |
|
|
|
201
201
|
"which customers have X enabled", "service toggles by org",
|
|
202
202
|
"is this URL blocked", "is this domain blocked", "is this address blocked",
|
|
203
203
|
"check this asset", "asset check", "lookup asset status",
|
|
204
|
+
"what is ARCHIVE_ORG", "what does PAGE mean", "list asset types",
|
|
205
|
+
"supported asset types", "asset type mapping", "asset type enum",
|
|
206
|
+
"what asset types are there", "human readable asset type",
|
|
204
207
|
"how many takedowns", "takedowns in the last", "threats taken down",
|
|
205
208
|
"across all clients", "across all customers", "across all orgs",
|
|
206
209
|
"across all brands", "company-wide", "total takedowns", "total threats",
|
|
207
210
|
"total reports", "average takedowns", "average threats", "average per day",
|
|
208
211
|
"average per customer", "average per org", "rollup across customers",
|
|
209
|
-
"sum across orgs", "sum across customers"
|
|
212
|
+
"sum across orgs", "sum across customers",
|
|
213
|
+
"trends in org", "search for trends", "trend search", "look for trends",
|
|
214
|
+
"any trends", "trending threats", "spike in asset type",
|
|
215
|
+
"spike in threat volume", "coordinated attack", "spike check",
|
|
216
|
+
"anything unusual", "anything new for", "what's new for",
|
|
217
|
+
"employee being targeted", "spike on a sub-brand", "spike on a brand",
|
|
218
|
+
"sub-brand spike",
|
|
219
|
+
"list brands", "brands for org", "list sub-brands", "employee brands",
|
|
220
|
+
"individual brands", "brand list", "all brands in org".
|
|
210
221
|
allowed-tools:
|
|
211
222
|
- Bash
|
|
212
223
|
- Read
|
|
@@ -375,6 +386,29 @@ If any individual lookup fails, the CLI still prints results for the
|
|
|
375
386
|
successful ones, then exits non-zero so failures aren't silently
|
|
376
387
|
swallowed.
|
|
377
388
|
|
|
389
|
+
### \`asset types\` \u2014 List every supported asset type and what it means
|
|
390
|
+
|
|
391
|
+
When a user asks "what is \`ARCHIVE_ORG\`?", "what does \`PAGE\` mean?",
|
|
392
|
+
or "what asset types does ChainPatrol support?" \u2014 don't guess. Run:
|
|
393
|
+
|
|
394
|
+
\`\`\`bash
|
|
395
|
+
chainpatrol asset types
|
|
396
|
+
chainpatrol --json asset types
|
|
397
|
+
\`\`\`
|
|
398
|
+
|
|
399
|
+
The mapping is bundled with the CLI (no API or auth required), so this
|
|
400
|
+
is safe to run anywhere. Each row is \`{ type, label, description }\`:
|
|
401
|
+
\`type\` is the canonical enum value used by every \`--asset-type\` flag
|
|
402
|
+
(\`asset list\`, \`threats list\`, \`takedowns list\`, \`detections list\`,
|
|
403
|
+
\`reports list\`, \`orgs assets list\`); \`label\` is the human-friendly
|
|
404
|
+
display name (\`ARCHIVE_ORG\` \u2192 \`Archive.org\`, \`PAGE\` \u2192 \`Page\`,
|
|
405
|
+
\`FIVE_HUNDRED_PX\` \u2192 \`500px\`); \`description\` is a one-line note for
|
|
406
|
+
unobvious entries.
|
|
407
|
+
|
|
408
|
+
Use this whenever you need to translate between enum and display name,
|
|
409
|
+
validate that a type the user mentioned is real, or enumerate options
|
|
410
|
+
before constructing a filter.
|
|
411
|
+
|
|
378
412
|
### \`configs list\` \u2014 List detection configurations
|
|
379
413
|
|
|
380
414
|
Requires authentication and an organization slug.
|
|
@@ -732,6 +766,36 @@ takedowns?", reach for \`orgs list\` \u2014 it's the only command that exposes
|
|
|
732
766
|
service flags across multiple orgs in one call. Run it in \`--json\` mode
|
|
733
767
|
and summarize patterns by service or by subscription tier.
|
|
734
768
|
|
|
769
|
+
### \`brands list\` \u2014 List the brands (sub-brands) belonging to an org
|
|
770
|
+
|
|
771
|
+
Returns every brand for the org the caller is authenticated as, both
|
|
772
|
+
parent / product brands (\`type: "ORGANIZATION"\`) and individual /
|
|
773
|
+
employee brands (\`type: "INDIVIDUAL"\`). Soft-deleted brands are excluded.
|
|
774
|
+
The endpoint is org-scoped and inferred from auth \u2014 there's no \`--org\`
|
|
775
|
+
flag \u2014 so org-scoped API keys, user API keys, and user sessions all work
|
|
776
|
+
without extra arguments.
|
|
777
|
+
|
|
778
|
+
\`\`\`bash
|
|
779
|
+
chainpatrol brands list # all brands
|
|
780
|
+
chainpatrol brands list --type INDIVIDUAL # employee / person brands only
|
|
781
|
+
chainpatrol brands list --type ORGANIZATION # product / parent brands only
|
|
782
|
+
chainpatrol --json brands list # machine-readable
|
|
783
|
+
\`\`\`
|
|
784
|
+
|
|
785
|
+
Each entry has \`{ id, slug, name, type, description, brandGroupId, createdAt }\`.
|
|
786
|
+
Use this when you need to:
|
|
787
|
+
|
|
788
|
+
- Distinguish employee vs. product brands ahead of time \u2014 e.g. while
|
|
789
|
+
running a trend search and you want to highlight which spiking
|
|
790
|
+
sub-brands are employees so the user gets a direct heads-up to the
|
|
791
|
+
person involved.
|
|
792
|
+
- Resolve a brand name the user mentions to its \`slug\` for use with
|
|
793
|
+
\`metrics organization --brand-slug <slug>\` or as a \`brandId\` for
|
|
794
|
+
\`takedowns list --brand <id>\`.
|
|
795
|
+
- Audit a customer's setup \u2014 "how many employee brands does this org
|
|
796
|
+
protect?" or "are there brands the org set up but never wired into a
|
|
797
|
+
detection config?"
|
|
798
|
+
|
|
735
799
|
### \`metrics summary | found | breakdown | organization\` \u2014 Org metrics for spike/drop analysis
|
|
736
800
|
|
|
737
801
|
#### Decision rule \u2014 read this before picking a metrics subcommand
|
|
@@ -1416,6 +1480,190 @@ Until a dedicated probe command exists:
|
|
|
1416
1480
|
In both cases, if liveness looks miscalibrated for a class of assets,
|
|
1417
1481
|
notify ChainPatrol engineering \u2014 the checker likely needs a tuning pass for
|
|
1418
1482
|
that platform.
|
|
1483
|
+
|
|
1484
|
+
## Organization Trend Search Guide
|
|
1485
|
+
|
|
1486
|
+
Trend search asks "what's changed recently for this org?" \u2014 the answer can
|
|
1487
|
+
surface coordinated attacks, new attack channels, or campaigns targeting
|
|
1488
|
+
specific people, **before** they show up as a healthcheck failure. Unlike
|
|
1489
|
+
the HealthCheck Guide above (which grades single signals against
|
|
1490
|
+
thresholds), trend search compares a recent window to a baseline window
|
|
1491
|
+
and flags ratios, not absolute counts. A trend is interesting even when
|
|
1492
|
+
the pipeline is keeping up with it \u2014 the user usually wants to know.
|
|
1493
|
+
|
|
1494
|
+
When the user asks something like:
|
|
1495
|
+
|
|
1496
|
+
- "are there any trends in org X?"
|
|
1497
|
+
- "search for trends in <org>"
|
|
1498
|
+
- "anything unusual happening with <org> lately?"
|
|
1499
|
+
- "any spikes for <org>?"
|
|
1500
|
+
- "what's new for <org> this week?"
|
|
1501
|
+
- "is anyone targeting <org>'s employees more than usual?"
|
|
1502
|
+
- "are we seeing more YouTube/Telegram/etc. threats for <org>?"
|
|
1503
|
+
|
|
1504
|
+
\u2026run the three trend checks below in parallel (single message, multiple
|
|
1505
|
+
Bash calls) and surface anything where the current rate is \u2265 2\xD7 the
|
|
1506
|
+
baseline AND clears a small absolute floor.
|
|
1507
|
+
|
|
1508
|
+
### How to compute "current vs. baseline" rates
|
|
1509
|
+
|
|
1510
|
+
Pick two non-overlapping windows: a **current** window the user cares about
|
|
1511
|
+
(default last 7 days) and a **baseline** window ending where the current
|
|
1512
|
+
one starts (default the prior ~90 days, i.e. \`current_start - 90d\` \u2192
|
|
1513
|
+
\`current_start - 1d\`). For any breakdown bucket, compute:
|
|
1514
|
+
|
|
1515
|
+
\`\`\`
|
|
1516
|
+
current_per_day = current_count / current_window_days
|
|
1517
|
+
baseline_per_day = baseline_count / baseline_window_days
|
|
1518
|
+
ratio = current_per_day / max(baseline_per_day, epsilon)
|
|
1519
|
+
\`\`\`
|
|
1520
|
+
|
|
1521
|
+
Flag a bucket if \`ratio >= 2\` AND \`current_count >= 5\`. The floor
|
|
1522
|
+
suppresses noise on orgs with near-zero baseline activity (a single new
|
|
1523
|
+
YouTube report jumping the rate from 0 to 1/day shouldn't trigger). If
|
|
1524
|
+
the user gives a different window ("last 3 days", "this month"), adjust
|
|
1525
|
+
both windows proportionally and keep the same ratio threshold.
|
|
1526
|
+
|
|
1527
|
+
### Trend 1 \u2014 Spike in a specific asset type
|
|
1528
|
+
|
|
1529
|
+
A sudden jump in threats on a platform the org doesn't usually see
|
|
1530
|
+
traffic on ("normally you don't get targeted on YouTube much, but now
|
|
1531
|
+
there's a spike there") is worth flagging \u2014 it usually means an attacker
|
|
1532
|
+
has discovered a new channel that works for them. Even when the absolute
|
|
1533
|
+
number is small, the *ratio* against the org's normal mix is what
|
|
1534
|
+
matters.
|
|
1535
|
+
|
|
1536
|
+
Use \`metrics breakdown --by type\` over both windows:
|
|
1537
|
+
|
|
1538
|
+
\`\`\`bash
|
|
1539
|
+
# Current window \u2014 last 7 days, broken out by asset type
|
|
1540
|
+
chainpatrol --json metrics breakdown --org <slug> --by type \\
|
|
1541
|
+
--from <YYYY-MM-DD 7d ago> --to <YYYY-MM-DD today>
|
|
1542
|
+
|
|
1543
|
+
# Baseline window \u2014 prior ~90 days ending where the current window starts
|
|
1544
|
+
chainpatrol --json metrics breakdown --org <slug> --by type \\
|
|
1545
|
+
--from <YYYY-MM-DD 97d ago> --to <YYYY-MM-DD 8d ago>
|
|
1546
|
+
\`\`\`
|
|
1547
|
+
|
|
1548
|
+
Each entry in \`.points\` has \`{ type, count }\`. Join the two windows on
|
|
1549
|
+
\`type\`, apply the ratio rule above, and report each flagged type.
|
|
1550
|
+
|
|
1551
|
+
Cross-reference any flagged type with \`configs list --org <slug>\` \u2014 if
|
|
1552
|
+
the spike is on Twitter / X but \`twitter_post_search\` is disabled, the
|
|
1553
|
+
detection source for that channel isn't even running for this org and
|
|
1554
|
+
the spike is being caught by something else (likely customer reports);
|
|
1555
|
+
suggest turning it on.
|
|
1556
|
+
|
|
1557
|
+
### Trend 2 \u2014 Spike in overall threat volume
|
|
1558
|
+
|
|
1559
|
+
A sharp rise in total threats across the org \u2014 regardless of type or
|
|
1560
|
+
brand \u2014 usually means a coordinated attack or campaign is underway. This
|
|
1561
|
+
isn't a healthcheck (reviewing and takedowns may be keeping up just
|
|
1562
|
+
fine), but the security team still wants to know so they can warn
|
|
1563
|
+
customers / employees / partners.
|
|
1564
|
+
|
|
1565
|
+
\`\`\`bash
|
|
1566
|
+
# Daily volume for the last ~6 weeks \u2014 enough to eyeball a baseline AND
|
|
1567
|
+
# see the spike on the right edge of the series.
|
|
1568
|
+
chainpatrol --json metrics breakdown --org <slug> --by day \\
|
|
1569
|
+
--from <YYYY-MM-DD 42d ago> --to <YYYY-MM-DD today>
|
|
1570
|
+
\`\`\`
|
|
1571
|
+
|
|
1572
|
+
Read \`.points\` (one entry per day). Take the trailing 7-day average and
|
|
1573
|
+
compare against the prior ~5 weeks (the same 2\xD7 / floor rule). For round
|
|
1574
|
+
totals to quote to the user, also pull:
|
|
1575
|
+
|
|
1576
|
+
\`\`\`bash
|
|
1577
|
+
# Two calls \u2014 current and baseline windows \u2014 using --include to skip
|
|
1578
|
+
# the per-type / per-day series and keep the call cheap.
|
|
1579
|
+
chainpatrol --json metrics organization --org <slug> \\
|
|
1580
|
+
--include reports,newThreats,threatsWatchlisted \\
|
|
1581
|
+
--from <current_from> --to <current_to>
|
|
1582
|
+
|
|
1583
|
+
chainpatrol --json metrics organization --org <slug> \\
|
|
1584
|
+
--include reports,newThreats,threatsWatchlisted \\
|
|
1585
|
+
--from <baseline_from> --to <baseline_to>
|
|
1586
|
+
\`\`\`
|
|
1587
|
+
|
|
1588
|
+
When volume spikes broadly, also skim \`reports list --reported-by-customer\`
|
|
1589
|
+
for the same window \u2014 a wave of customer reports often arrives a few hours
|
|
1590
|
+
ahead of automated detection on a real coordinated push.
|
|
1591
|
+
|
|
1592
|
+
### Trend 3 \u2014 Spike on a specific sub-brand (especially employee brands)
|
|
1593
|
+
|
|
1594
|
+
Sub-brands the org has set up for individual people \u2014 employees,
|
|
1595
|
+
executives, public figures \u2014 are high-signal targets. A sudden spike on
|
|
1596
|
+
one usually means an attacker is impersonating that person specifically,
|
|
1597
|
+
which is materially different from a generic phishing wave and usually
|
|
1598
|
+
warrants a direct heads-up to the person involved. In the database these
|
|
1599
|
+
are \`Brand\` rows with \`type: INDIVIDUAL\` (vs. \`ORGANIZATION\` for
|
|
1600
|
+
product / parent brands).
|
|
1601
|
+
|
|
1602
|
+
**Step 1: Pull the brand roster** so you can label each spike as
|
|
1603
|
+
"employee" or "product" without asking the user.
|
|
1604
|
+
|
|
1605
|
+
\`\`\`bash
|
|
1606
|
+
chainpatrol --json brands list
|
|
1607
|
+
\`\`\`
|
|
1608
|
+
|
|
1609
|
+
Build a map of \`slug \u2192 type\` from the response so the next step's
|
|
1610
|
+
findings can be tagged \`(employee)\` or \`(product)\` automatically.
|
|
1611
|
+
|
|
1612
|
+
**Step 2: Run the breakdown over both windows.**
|
|
1613
|
+
|
|
1614
|
+
\`\`\`bash
|
|
1615
|
+
# Current window \u2014 last 7 days, broken out by sub-brand
|
|
1616
|
+
chainpatrol --json metrics breakdown --org <slug> --by brand \\
|
|
1617
|
+
--from <YYYY-MM-DD 7d ago> --to <YYYY-MM-DD today>
|
|
1618
|
+
|
|
1619
|
+
# Baseline window \u2014 prior ~90 days
|
|
1620
|
+
chainpatrol --json metrics breakdown --org <slug> --by brand \\
|
|
1621
|
+
--from <YYYY-MM-DD 97d ago> --to <YYYY-MM-DD 8d ago>
|
|
1622
|
+
\`\`\`
|
|
1623
|
+
|
|
1624
|
+
Each entry in \`.points\` has \`{ brandId, brandSlug, brandName, count }\`.
|
|
1625
|
+
Apply the ratio rule, join against the \`slug \u2192 type\` map from Step 1,
|
|
1626
|
+
and report each spiking sub-brand by name plus its type.
|
|
1627
|
+
|
|
1628
|
+
**Step 3: Drill into the flagged brand** with \`metrics organization
|
|
1629
|
+
--brand-slug <slug>\` for a per-day / per-type breakdown scoped to that
|
|
1630
|
+
brand only. The server applies the brand filter to the scalar metrics
|
|
1631
|
+
(\`newThreats\`, \`takedownsFiled\`, etc.) AND to \`blockedByType\` /
|
|
1632
|
+
\`blockedByDay\`, so the time series and per-asset-type counts are real
|
|
1633
|
+
brand-only data:
|
|
1634
|
+
|
|
1635
|
+
\`\`\`bash
|
|
1636
|
+
chainpatrol --json metrics organization --org <slug> \\
|
|
1637
|
+
--brand-slug <brand-slug> \\
|
|
1638
|
+
--include newThreats,blockedByType,blockedByDay \\
|
|
1639
|
+
--from <YYYY-MM-DD 7d ago> --to <YYYY-MM-DD today>
|
|
1640
|
+
\`\`\`
|
|
1641
|
+
|
|
1642
|
+
If multiple \`INDIVIDUAL\` brands are spiking at the same time, treat that
|
|
1643
|
+
as a stronger signal than a single one \u2014 likely a campaign targeting the
|
|
1644
|
+
company's people rather than a single impersonation. Prioritize those
|
|
1645
|
+
findings over single-brand spikes when summarizing.
|
|
1646
|
+
|
|
1647
|
+
### Reporting trend findings
|
|
1648
|
+
|
|
1649
|
+
Report each spiking bucket as its own finding with: the trend it falls
|
|
1650
|
+
under (asset type / overall volume / sub-brand), the current vs. baseline
|
|
1651
|
+
rate (e.g. "youtube: 12/day last 7d vs. 1.2/day prior 90d, \xD710
|
|
1652
|
+
baseline"), and a one-line follow-up:
|
|
1653
|
+
|
|
1654
|
+
- Asset-type spike \u2192 check \`configs list\` for the matching detection
|
|
1655
|
+
source and turn it on if it's off.
|
|
1656
|
+
- Overall-volume spike \u2192 flag a possible coordinated campaign; suggest
|
|
1657
|
+
the security team look at the recent \`reports list\` for shared
|
|
1658
|
+
infrastructure (sender, registrar, hosting).
|
|
1659
|
+
- Sub-brand spike \u2192 look up \`type\` via \`brands list\`; if
|
|
1660
|
+
\`INDIVIDUAL\`, suggest a direct heads-up to that person; if
|
|
1661
|
+
\`ORGANIZATION\`, suggest a product-team alert.
|
|
1662
|
+
|
|
1663
|
+
If none of the three trends fire above the 2\xD7 / floor thresholds, say so
|
|
1664
|
+
explicitly ("no significant trends in the last 7 days vs. the prior 90")
|
|
1665
|
+
rather than dumping every breakdown number \u2014 the absence is the
|
|
1666
|
+
finding.
|
|
1419
1667
|
`;
|
|
1420
1668
|
}
|
|
1421
1669
|
function getBundledSkillVersion() {
|
|
@@ -66,6 +66,7 @@ _chainpatrol() {
|
|
|
66
66
|
asset_subcommands=(
|
|
67
67
|
'check:Check asset status against ChainPatrol'
|
|
68
68
|
'list:List assets in the global ChainPatrol blocklist'
|
|
69
|
+
'types:List supported asset types and their human-readable labels'
|
|
69
70
|
)
|
|
70
71
|
_describe 'subcommand' asset_subcommands
|
|
71
72
|
;;
|
|
@@ -161,7 +162,7 @@ var BASH_COMPLETION = `_chainpatrol() {
|
|
|
161
162
|
return 0
|
|
162
163
|
;;
|
|
163
164
|
asset)
|
|
164
|
-
COMPREPLY=( $(compgen -W "check list" -- "\${cur}") )
|
|
165
|
+
COMPREPLY=( $(compgen -W "check list types" -- "\${cur}") )
|
|
165
166
|
return 0
|
|
166
167
|
;;
|
|
167
168
|
configs)
|