@anysiteio/agent-skills 1.0.0 → 1.2.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/.claude-plugin/marketplace.json +25 -1
- package/.claude-plugin/plugin.json +3 -1
- package/README.md +28 -8
- package/bin/install.js +3 -1
- package/package.json +5 -3
- package/skills/anysite-cli/README.md +419 -0
- package/skills/anysite-cli/SKILL.md +292 -0
- package/skills/anysite-cli/references/api-reference.md +160 -0
- package/skills/anysite-cli/references/dataset-guide.md +434 -0
- package/skills/skill-audit/README.md +287 -0
- package/skills/skill-audit/SKILL.md +274 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
# Dataset Pipeline & Database Guide
|
|
2
|
+
|
|
3
|
+
## Dataset YAML Configuration
|
|
4
|
+
|
|
5
|
+
### Full Structure
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
name: my-dataset
|
|
9
|
+
description: Optional description
|
|
10
|
+
|
|
11
|
+
sources:
|
|
12
|
+
- id: source_id # Unique identifier
|
|
13
|
+
endpoint: /api/... # API endpoint path
|
|
14
|
+
params: {} # Static API parameters
|
|
15
|
+
from_file: inputs.txt # Input file (txt/JSONL/CSV)
|
|
16
|
+
file_field: column_name # CSV column to extract
|
|
17
|
+
input_key: param_name # API parameter for input values
|
|
18
|
+
input_template: {} # Transform values before API call
|
|
19
|
+
dependency: # Link to parent source
|
|
20
|
+
from_source: parent_id
|
|
21
|
+
field: urn.value # Dot-notation field to extract
|
|
22
|
+
match_by: name # Alternative: fuzzy match by name
|
|
23
|
+
dedupe: true # Remove duplicate values
|
|
24
|
+
parallel: 3 # Concurrent requests
|
|
25
|
+
rate_limit: "10/s" # Rate limiting
|
|
26
|
+
on_error: skip # stop or skip
|
|
27
|
+
transform: # Post-collection transform (for exports only)
|
|
28
|
+
filter: '.count > 10' # Safe filter expression
|
|
29
|
+
fields: [name, url] # Field selection with aliases
|
|
30
|
+
add_columns: # Static columns to inject
|
|
31
|
+
batch: "q1-2026"
|
|
32
|
+
export: # Per-source export destinations
|
|
33
|
+
- type: file
|
|
34
|
+
path: ./output/{{source}}-{{date}}.csv
|
|
35
|
+
format: csv # json, jsonl, csv
|
|
36
|
+
- type: webhook
|
|
37
|
+
url: https://example.com/hook
|
|
38
|
+
headers: {X-Token: abc}
|
|
39
|
+
db_load: # Database loading config
|
|
40
|
+
table: custom_name # Override table name
|
|
41
|
+
fields: [name, url] # Fields to include
|
|
42
|
+
exclude: [_input_value] # Fields to exclude
|
|
43
|
+
|
|
44
|
+
storage:
|
|
45
|
+
format: parquet
|
|
46
|
+
path: ./data/
|
|
47
|
+
partition_by: [source_id, collected_date]
|
|
48
|
+
|
|
49
|
+
schedule: # Collection schedule
|
|
50
|
+
cron: "0 9 * * *" # Standard cron expression
|
|
51
|
+
|
|
52
|
+
notifications: # Webhook notifications
|
|
53
|
+
on_complete:
|
|
54
|
+
- url: "https://hooks.slack.com/xxx"
|
|
55
|
+
headers: {X-Token: abc}
|
|
56
|
+
on_failure:
|
|
57
|
+
- url: "https://alerts.example.com/fail"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Three Source Types
|
|
61
|
+
|
|
62
|
+
**Independent** — single API call with static params:
|
|
63
|
+
```yaml
|
|
64
|
+
- id: search_results
|
|
65
|
+
endpoint: /api/linkedin/search/users
|
|
66
|
+
params:
|
|
67
|
+
keywords: "software engineer"
|
|
68
|
+
count: 50
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**from_file** — iterate over values from a file:
|
|
72
|
+
```yaml
|
|
73
|
+
- id: companies
|
|
74
|
+
endpoint: /api/linkedin/company
|
|
75
|
+
from_file: companies.txt # One value per line
|
|
76
|
+
input_key: company # API parameter name
|
|
77
|
+
parallel: 3
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Dependent** — extract values from parent source output:
|
|
81
|
+
```yaml
|
|
82
|
+
- id: employees
|
|
83
|
+
endpoint: /api/linkedin/company/employees
|
|
84
|
+
dependency:
|
|
85
|
+
from_source: companies # Parent source ID
|
|
86
|
+
field: urn.value # Dot-notation path in parent records
|
|
87
|
+
dedupe: true # Deduplicate extracted values
|
|
88
|
+
input_key: companies # API parameter name
|
|
89
|
+
input_template: # Transform value before API call
|
|
90
|
+
companies:
|
|
91
|
+
- type: company
|
|
92
|
+
value: "{value}" # {value} is replaced with extracted value
|
|
93
|
+
count: 5
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Dependency Chains
|
|
97
|
+
|
|
98
|
+
Sources are topologically sorted — parents always run before children. Multi-level chains work automatically:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
companies → employees → profiles → posts → comments
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### input_template
|
|
105
|
+
|
|
106
|
+
Transforms extracted values before passing to the API. Use `{value}` placeholder:
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
input_template:
|
|
110
|
+
urn: "urn:li:fsd_profile:{value}"
|
|
111
|
+
count: 5
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
If `field: urn.value` extracts `"ACoAABCDEF"`, the API receives:
|
|
115
|
+
```json
|
|
116
|
+
{"urn": "urn:li:fsd_profile:ACoAABCDEF", "count": 5}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Dot-Notation Field Extraction
|
|
120
|
+
|
|
121
|
+
When Parquet stores nested objects as JSON strings, dot-notation traverses them:
|
|
122
|
+
|
|
123
|
+
- `urn.value` — parses JSON string in `urn` field, extracts `.value`
|
|
124
|
+
- `experience[0].company_urn` — array index + nested field
|
|
125
|
+
- `internal_id.value` — nested object access
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Collection Commands
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Preview collection plan with estimated request counts
|
|
133
|
+
anysite dataset collect dataset.yaml --dry-run
|
|
134
|
+
|
|
135
|
+
# Full collection
|
|
136
|
+
anysite dataset collect dataset.yaml
|
|
137
|
+
|
|
138
|
+
# Collect and auto-load into database
|
|
139
|
+
anysite dataset collect dataset.yaml --load-db pg
|
|
140
|
+
|
|
141
|
+
# Incremental — skip inputs already collected
|
|
142
|
+
anysite dataset collect dataset.yaml --incremental --load-db pg
|
|
143
|
+
|
|
144
|
+
# Single source + its dependencies
|
|
145
|
+
anysite dataset collect dataset.yaml --source employees
|
|
146
|
+
|
|
147
|
+
# Quiet mode
|
|
148
|
+
anysite dataset collect dataset.yaml --quiet
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Provenance Tracking
|
|
152
|
+
|
|
153
|
+
Dependent and from_file records automatically get metadata columns:
|
|
154
|
+
- `_input_value` — the raw extracted value that produced this record
|
|
155
|
+
- `_parent_source` — parent source ID (dependent sources only)
|
|
156
|
+
|
|
157
|
+
These enable FK linking when loading into a database.
|
|
158
|
+
|
|
159
|
+
### Incremental Collection
|
|
160
|
+
|
|
161
|
+
With `--incremental`:
|
|
162
|
+
1. Independent sources: skipped if already collected today
|
|
163
|
+
2. Dependent/from_file sources: skips individual input values already in `metadata.json`
|
|
164
|
+
3. New values are still collected and tracked
|
|
165
|
+
|
|
166
|
+
### Storage Layout
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
<storage.path>/
|
|
170
|
+
raw/<source_id>/<YYYY-MM-DD>.parquet
|
|
171
|
+
metadata.json
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Query Commands (DuckDB)
|
|
177
|
+
|
|
178
|
+
Each source becomes a DuckDB view named after its ID.
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Direct SQL
|
|
182
|
+
anysite dataset query dataset.yaml --sql "SELECT * FROM companies LIMIT 10"
|
|
183
|
+
|
|
184
|
+
# Shorthand: --source auto-generates SELECT
|
|
185
|
+
anysite dataset query dataset.yaml --source profiles
|
|
186
|
+
|
|
187
|
+
# Dot-notation field extraction
|
|
188
|
+
anysite dataset query dataset.yaml --source profiles \
|
|
189
|
+
--fields "name, urn.value AS urn_id, headline"
|
|
190
|
+
# Generates: SELECT name, json_extract_string(urn, '$.value') AS urn_id, headline FROM profiles
|
|
191
|
+
|
|
192
|
+
# Output options
|
|
193
|
+
anysite dataset query dataset.yaml --sql "SELECT * FROM companies" \
|
|
194
|
+
--format csv --output companies.csv
|
|
195
|
+
|
|
196
|
+
# Interactive SQL shell
|
|
197
|
+
anysite dataset query dataset.yaml --interactive
|
|
198
|
+
|
|
199
|
+
# Column statistics
|
|
200
|
+
anysite dataset stats dataset.yaml --source companies
|
|
201
|
+
|
|
202
|
+
# Data profiling (completeness, record counts)
|
|
203
|
+
anysite dataset profile dataset.yaml
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Database Loading (load-db)
|
|
209
|
+
|
|
210
|
+
Load Parquet data into a relational database with automatic FK linking.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
anysite dataset load-db dataset.yaml -c <connection_name> [OPTIONS]
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Options
|
|
217
|
+
```
|
|
218
|
+
--connection, -c TEXT Database connection name (required)
|
|
219
|
+
--source, -s TEXT Load specific source + dependencies
|
|
220
|
+
--drop-existing Drop tables before creating
|
|
221
|
+
--dry-run Show plan without executing
|
|
222
|
+
--quiet, -q Suppress output
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### What load-db Does
|
|
226
|
+
|
|
227
|
+
1. Reads Parquet files for each source (in topological order)
|
|
228
|
+
2. Infers SQL schema from data (integer, float, text, json, etc.)
|
|
229
|
+
3. Creates tables with auto-increment `id` primary key
|
|
230
|
+
4. Inserts rows, tracking which `_input_value` maps to which `id`
|
|
231
|
+
5. For child sources: adds `{parent_source}_id` FK column using provenance
|
|
232
|
+
|
|
233
|
+
### db_load Config
|
|
234
|
+
|
|
235
|
+
Control which fields go to the database per source:
|
|
236
|
+
|
|
237
|
+
```yaml
|
|
238
|
+
db_load:
|
|
239
|
+
table: people # Custom table name (default: source ID)
|
|
240
|
+
fields: # Explicit field list
|
|
241
|
+
- name
|
|
242
|
+
- url
|
|
243
|
+
- urn.value AS urn_id # Dot-notation with alias
|
|
244
|
+
- experience # JSON columns stored as TEXT/JSONB
|
|
245
|
+
exclude: # Fields to skip (default: _input_value, _parent_source)
|
|
246
|
+
- _input_value
|
|
247
|
+
- _parent_source
|
|
248
|
+
- raw_html
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Without `db_load`: all fields except `_input_value` and `_parent_source` are loaded.
|
|
252
|
+
|
|
253
|
+
### FK Linking Example
|
|
254
|
+
|
|
255
|
+
Given: companies → employees → posts
|
|
256
|
+
|
|
257
|
+
Result in database:
|
|
258
|
+
- `companies` table: `id`, name, url, ...
|
|
259
|
+
- `employees` table: `id`, name, ..., `companies_id` (FK to companies.id)
|
|
260
|
+
- `posts` table: `id`, text, ..., `employees_id` (FK to employees.id)
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Database Commands (anysite db)
|
|
265
|
+
|
|
266
|
+
### Connection Management
|
|
267
|
+
```bash
|
|
268
|
+
anysite db add <name> # Interactive add
|
|
269
|
+
anysite db list # List all connections
|
|
270
|
+
anysite db test <name> # Test connectivity
|
|
271
|
+
anysite db info <name> # Show connection details
|
|
272
|
+
anysite db remove <name> # Delete connection
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Connections stored in `~/.anysite/connections.yaml`. Passwords use env var references for security.
|
|
276
|
+
|
|
277
|
+
### Schema Inspection
|
|
278
|
+
```bash
|
|
279
|
+
anysite db schema <name> # List all tables
|
|
280
|
+
anysite db schema <name> --table users # Show columns
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Data Operations
|
|
284
|
+
```bash
|
|
285
|
+
# Insert from stdin (auto-create table)
|
|
286
|
+
cat data.jsonl | anysite db insert <name> --table users --stdin --auto-create
|
|
287
|
+
|
|
288
|
+
# Insert from file
|
|
289
|
+
anysite db insert <name> --table users --file data.jsonl
|
|
290
|
+
|
|
291
|
+
# Upsert (update on conflict)
|
|
292
|
+
anysite db upsert <name> --table users --conflict-columns id --stdin
|
|
293
|
+
|
|
294
|
+
# Insert with conflict handling
|
|
295
|
+
anysite db insert <name> --table users --file data.jsonl \
|
|
296
|
+
--on-conflict ignore --conflict-columns email
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### SQL Queries
|
|
300
|
+
```bash
|
|
301
|
+
anysite db query <name> --sql "SELECT * FROM users" --format table
|
|
302
|
+
anysite db query <name> --file report.sql --format csv --output report.csv
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Supported Databases
|
|
306
|
+
- **SQLite** — `--type sqlite --path ./data.db`
|
|
307
|
+
- **PostgreSQL** — `--type postgres --host localhost --database mydb --user app --password-env DB_PASS`
|
|
308
|
+
- PostgreSQL also supports `--url-env DATABASE_URL` for connection strings
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Per-Source Transform
|
|
313
|
+
|
|
314
|
+
Transforms apply to export destinations only. Parquet always stores full records (needed for dependency resolution).
|
|
315
|
+
|
|
316
|
+
```yaml
|
|
317
|
+
transform:
|
|
318
|
+
filter: '.employee_count > 10 and .status == "active"'
|
|
319
|
+
fields:
|
|
320
|
+
- name
|
|
321
|
+
- url
|
|
322
|
+
- urn.value AS urn_id # Dot-notation with alias
|
|
323
|
+
- employee_count
|
|
324
|
+
add_columns:
|
|
325
|
+
batch: "q1-2026"
|
|
326
|
+
source: "linkedin"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Filter Syntax
|
|
330
|
+
Safe expression parser (no `eval()`). Supported operators: `==`, `!=`, `>`, `<`, `>=`, `<=`. Connectors: `and`, `or`. Values: strings (`"..."`), numbers, `null`.
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
.field > 10
|
|
334
|
+
.status == "active"
|
|
335
|
+
.location != ""
|
|
336
|
+
.name != null
|
|
337
|
+
.count > 5 and .count < 100
|
|
338
|
+
.status == "active" or .status == "pending"
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Per-Source Export
|
|
344
|
+
|
|
345
|
+
Export destinations run after Parquet write. Transform is applied to export records if configured.
|
|
346
|
+
|
|
347
|
+
### File Export
|
|
348
|
+
```yaml
|
|
349
|
+
export:
|
|
350
|
+
- type: file
|
|
351
|
+
path: ./output/{{source}}-{{date}}.csv
|
|
352
|
+
format: csv # json, jsonl, csv
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Template variables: `{{date}}` (YYYY-MM-DD), `{{datetime}}` (ISO), `{{source}}` (source ID), `{{dataset}}` (dataset name).
|
|
356
|
+
|
|
357
|
+
### Webhook Export
|
|
358
|
+
```yaml
|
|
359
|
+
export:
|
|
360
|
+
- type: webhook
|
|
361
|
+
url: https://example.com/hook
|
|
362
|
+
headers:
|
|
363
|
+
X-Token: abc
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Sends POST with JSON body: `{dataset, source, count, records, timestamp}`.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Run History & Logs
|
|
371
|
+
|
|
372
|
+
Every `collect` run is automatically recorded in SQLite (`~/.anysite/dataset_history.db`).
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
# View run history
|
|
376
|
+
anysite dataset history my-dataset
|
|
377
|
+
anysite dataset history my-dataset --limit 5
|
|
378
|
+
|
|
379
|
+
# View logs for a specific run
|
|
380
|
+
anysite dataset logs my-dataset --run 42
|
|
381
|
+
|
|
382
|
+
# View latest run logs
|
|
383
|
+
anysite dataset logs my-dataset
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Scheduling
|
|
389
|
+
|
|
390
|
+
Generate cron or systemd entries from the `schedule.cron` config.
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
# Crontab entry (default)
|
|
394
|
+
anysite dataset schedule dataset.yaml --incremental --load-db pg
|
|
395
|
+
|
|
396
|
+
# Systemd timer units
|
|
397
|
+
anysite dataset schedule dataset.yaml --systemd --incremental --load-db pg
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Output example:
|
|
401
|
+
```
|
|
402
|
+
0 9 * * * /path/to/anysite dataset collect dataset.yaml --incremental --load-db pg >> ~/.anysite/logs/my-dataset_cron.log 2>&1
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Notifications
|
|
408
|
+
|
|
409
|
+
Webhook notifications sent on collection complete or failure.
|
|
410
|
+
|
|
411
|
+
```yaml
|
|
412
|
+
notifications:
|
|
413
|
+
on_complete:
|
|
414
|
+
- url: "https://hooks.slack.com/services/xxx"
|
|
415
|
+
headers: {Authorization: "Bearer token"}
|
|
416
|
+
on_failure:
|
|
417
|
+
- url: "https://alerts.example.com/fail"
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Payload: `{event: "complete"|"failure", dataset, timestamp, record_count, source_count, duration, error}`.
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Reset Incremental State
|
|
425
|
+
|
|
426
|
+
Clear collected input tracking to force re-collection.
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
# Reset all sources
|
|
430
|
+
anysite dataset reset-cursor dataset.yaml
|
|
431
|
+
|
|
432
|
+
# Reset specific source
|
|
433
|
+
anysite dataset reset-cursor dataset.yaml --source profiles
|
|
434
|
+
```
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Skill Audit
|
|
2
|
+
|
|
3
|
+
Static security auditor for Claude Code skills, commands, and plugins. Detects dangerous patterns — excessive permissions, hooks, prompt injection, credential access, privilege escalation — before you enable a skill.
|
|
4
|
+
|
|
5
|
+
**Developer:** Anysite Skills Contributors
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The Skill Audit skill performs read-only static security analysis of Claude Code skills, commands, and plugins. It analyzes SKILL.md files, supporting scripts, and hooks to identify security risks before installation.
|
|
10
|
+
|
|
11
|
+
Works with both local files and remote GitHub repositories.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### Prerequisites
|
|
16
|
+
|
|
17
|
+
- **Claude Code** - Version with skill support enabled
|
|
18
|
+
- **Anysite Skills Marketplace** - Already added if you're viewing this
|
|
19
|
+
|
|
20
|
+
### Install from Marketplace
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Add the marketplace (if not already added)
|
|
24
|
+
/plugin marketplace add https://github.com/anysiteio/agent-skills
|
|
25
|
+
|
|
26
|
+
# Install the skill
|
|
27
|
+
/plugin install skill-audit@anysite-skills
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Permissions
|
|
31
|
+
|
|
32
|
+
The skill requires these permissions (automatically granted during installation):
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"permissions": {
|
|
37
|
+
"allow": [
|
|
38
|
+
"Skill(skill-audit)",
|
|
39
|
+
"Skill(skill-audit:*)"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
For stricter WebFetch domain enforcement (recommended):
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"permissions": {
|
|
50
|
+
"allow": [
|
|
51
|
+
"Skill(skill-audit)",
|
|
52
|
+
"Skill(skill-audit:*)",
|
|
53
|
+
"WebFetch(domain:api.github.com)",
|
|
54
|
+
"WebFetch(domain:raw.githubusercontent.com)"
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
### Quick Start
|
|
63
|
+
|
|
64
|
+
Once installed, invoke the skill to audit targets:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Audit a specific skill directory
|
|
68
|
+
/skill-audit .claude/skills/my-skill
|
|
69
|
+
|
|
70
|
+
# Audit a specific command file
|
|
71
|
+
/skill-audit .claude/commands/deploy.md
|
|
72
|
+
|
|
73
|
+
# Audit a skill by name (searches .claude/skills/ and .claude/commands/)
|
|
74
|
+
/skill-audit my-skill
|
|
75
|
+
|
|
76
|
+
# Audit ALL skills and commands in the current project
|
|
77
|
+
/skill-audit
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Remote Audit (GitHub)
|
|
81
|
+
|
|
82
|
+
Audit skills from GitHub before installing them:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Audit a GitHub repository
|
|
86
|
+
/skill-audit https://github.com/user/repo
|
|
87
|
+
|
|
88
|
+
# Audit a specific directory on GitHub
|
|
89
|
+
/skill-audit https://github.com/user/repo/tree/main/.claude/skills/my-skill
|
|
90
|
+
|
|
91
|
+
# Audit a single file from GitHub
|
|
92
|
+
/skill-audit https://github.com/user/repo/blob/main/.claude/commands/deploy.md
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## What It Checks
|
|
96
|
+
|
|
97
|
+
The auditor performs a comprehensive 5-phase static analysis:
|
|
98
|
+
|
|
99
|
+
### Analysis Phases
|
|
100
|
+
|
|
101
|
+
| Phase | What It Analyzes |
|
|
102
|
+
|-------|------------------|
|
|
103
|
+
| **Discovery** | Inventories all files in the skill/plugin directory, detects plugin structure |
|
|
104
|
+
| **Frontmatter** | Checks `allowed-tools`, hooks, permissions, invocation settings |
|
|
105
|
+
| **Body Content** | Scans for dangerous tool references, settings manipulation, injection patterns, sensitive file access |
|
|
106
|
+
| **Supporting Files** | Examines scripts for network egress, credential access, code execution, persistence |
|
|
107
|
+
| **Hooks** | Detects hook definitions or installations, classifies by risk level |
|
|
108
|
+
|
|
109
|
+
### Detection Rules
|
|
110
|
+
|
|
111
|
+
| Rule ID | Severity | Description |
|
|
112
|
+
|---------|----------|-------------|
|
|
113
|
+
| **SKL-001a** | Medium | Hooks present in skill (require manual review) |
|
|
114
|
+
| **SKL-001b** | Critical | Hooks with dangerous patterns (network, credentials, persistence) |
|
|
115
|
+
| **SKL-002** | Critical/High | Prompt injection or dynamic injection patterns (`!` + backticks, `$(...)`) |
|
|
116
|
+
| **SKL-003** | High | Bash/WebFetch/broad wildcards in `allowed-tools` |
|
|
117
|
+
| **SKL-004** | Medium/High | Missing `disable-model-invocation` on side-effect skills |
|
|
118
|
+
| **SKL-005** | High | Dangerous patterns in supporting scripts |
|
|
119
|
+
| **SKL-006** | High | Instructions to change permissions, settings, or hooks |
|
|
120
|
+
|
|
121
|
+
### Risk Scoring
|
|
122
|
+
|
|
123
|
+
| Score Range | Risk Level | Recommendation |
|
|
124
|
+
|-------------|------------|----------------|
|
|
125
|
+
| **0** | Clean | No findings, safe to enable |
|
|
126
|
+
| **1-3** | Low-Medium | Review findings, likely safe |
|
|
127
|
+
| **4-6** | High | Needs attention before enabling |
|
|
128
|
+
| **7-8** | Critical | Do not enable without remediation |
|
|
129
|
+
| **9-10** | Severe | Multiple critical findings, likely malicious |
|
|
130
|
+
|
|
131
|
+
## Output Format
|
|
132
|
+
|
|
133
|
+
The skill generates detailed audit reports:
|
|
134
|
+
|
|
135
|
+
```markdown
|
|
136
|
+
# Skill Audit Report: my-skill
|
|
137
|
+
|
|
138
|
+
**Path:** .claude/skills/my-skill/
|
|
139
|
+
**Date:** 2026-01-30
|
|
140
|
+
**Risk Score:** 3/10
|
|
141
|
+
**Overall Severity:** Medium
|
|
142
|
+
|
|
143
|
+
## Summary
|
|
144
|
+
The skill uses WebFetch for external API calls but has no hooks or injection patterns.
|
|
145
|
+
|
|
146
|
+
## Findings
|
|
147
|
+
| # | ID | Severity | Finding | Location | Evidence |
|
|
148
|
+
|---|---|---|---|---|---|
|
|
149
|
+
| 1 | SKL-003 | Medium | WebFetch in allowed-tools | SKILL.md:4 | `allowed-tools: Read, WebFetch` |
|
|
150
|
+
|
|
151
|
+
## Hardening Recommendations
|
|
152
|
+
1. Remove WebFetch unless strictly required for the skill's purpose.
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Security Properties
|
|
156
|
+
|
|
157
|
+
This skill is designed with security as the top priority:
|
|
158
|
+
|
|
159
|
+
### Read-Only Analysis
|
|
160
|
+
- Uses ONLY `Read`, `Grep`, `Glob` tools (plus `WebFetch` for remote GitHub audits)
|
|
161
|
+
- Cannot modify files or execute commands
|
|
162
|
+
- No system state changes
|
|
163
|
+
|
|
164
|
+
### Anti-Injection Protection
|
|
165
|
+
- All audited content treated as untrusted malicious input
|
|
166
|
+
- Instructions found in audited files are flagged as findings
|
|
167
|
+
- Never follows or executes instructions from audited content
|
|
168
|
+
|
|
169
|
+
### Evidence Redaction
|
|
170
|
+
- Secrets, tokens, and API keys found in evidence are automatically masked
|
|
171
|
+
- Prevents accidental exposure of credentials in audit reports
|
|
172
|
+
- Shows only first 4 and last 4 characters of sensitive values
|
|
173
|
+
|
|
174
|
+
### Scoped WebFetch
|
|
175
|
+
- WebFetch restricted to `raw.githubusercontent.com` and `api.github.com`
|
|
176
|
+
- Only used for fetching remote skill files from GitHub
|
|
177
|
+
- No redirects followed, no links from fetched content used
|
|
178
|
+
- No arbitrary URL access
|
|
179
|
+
|
|
180
|
+
### Isolated Execution
|
|
181
|
+
- Runs in a forked subagent context (`context: fork`)
|
|
182
|
+
- Prevents context contamination from audited content
|
|
183
|
+
- Clean execution environment for each audit
|
|
184
|
+
|
|
185
|
+
### Manual-Only Invocation
|
|
186
|
+
- `disable-model-invocation: true` prevents auto-triggering
|
|
187
|
+
- Must be invoked explicitly via `/skill-audit`
|
|
188
|
+
- No automatic background scans
|
|
189
|
+
|
|
190
|
+
### Remote Audit Limits
|
|
191
|
+
- Maximum 20 files per remote audit
|
|
192
|
+
- 100 KB file size limit
|
|
193
|
+
- Large repositories require targeting a specific subdirectory
|
|
194
|
+
|
|
195
|
+
## Use Cases
|
|
196
|
+
|
|
197
|
+
### Pre-Installation Audit
|
|
198
|
+
Audit skills from GitHub before installing them:
|
|
199
|
+
```bash
|
|
200
|
+
/skill-audit https://github.com/third-party/suspicious-skill
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Regular Security Reviews
|
|
204
|
+
Audit all installed skills periodically:
|
|
205
|
+
```bash
|
|
206
|
+
/skill-audit
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Third-Party Skill Vetting
|
|
210
|
+
Review community-contributed skills before enabling:
|
|
211
|
+
```bash
|
|
212
|
+
/skill-audit .claude/skills/community-skill
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Command Safety Check
|
|
216
|
+
Verify custom commands don't have dangerous patterns:
|
|
217
|
+
```bash
|
|
218
|
+
/skill-audit .claude/commands/deploy.md
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## How It Works
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
User Request
|
|
225
|
+
↓
|
|
226
|
+
skill-audit Skill
|
|
227
|
+
↓
|
|
228
|
+
Static Analysis Engine
|
|
229
|
+
↓
|
|
230
|
+
Security Report (Read-only, no modifications)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The skill performs purely static analysis without executing any code from the audited target. All analysis is based on pattern matching, AST analysis, and heuristic detection.
|
|
234
|
+
|
|
235
|
+
## Key Features
|
|
236
|
+
|
|
237
|
+
**Comprehensive Detection**
|
|
238
|
+
- Hooks and auto-execution patterns
|
|
239
|
+
- Prompt injection attempts
|
|
240
|
+
- Excessive permissions
|
|
241
|
+
- Credential access patterns
|
|
242
|
+
- Privilege escalation attempts
|
|
243
|
+
|
|
244
|
+
**Multi-Target Support**
|
|
245
|
+
- Local skill directories
|
|
246
|
+
- Individual command files
|
|
247
|
+
- Remote GitHub repositories
|
|
248
|
+
- Bulk project audits
|
|
249
|
+
|
|
250
|
+
**Zero Side Effects**
|
|
251
|
+
- Read-only operation
|
|
252
|
+
- No file modifications
|
|
253
|
+
- No command execution
|
|
254
|
+
- Safe to run on any skill
|
|
255
|
+
|
|
256
|
+
**Detailed Reporting**
|
|
257
|
+
- Risk scoring (0-10 scale)
|
|
258
|
+
- Severity classification
|
|
259
|
+
- Specific evidence locations
|
|
260
|
+
- Hardening recommendations
|
|
261
|
+
|
|
262
|
+
## Structure
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
skill-audit/
|
|
266
|
+
├── SKILL.md # Skill definition + audit logic (single file)
|
|
267
|
+
└── README.md # This file
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Contributing
|
|
271
|
+
|
|
272
|
+
Found a security pattern that should be detected? Submit an issue or pull request at:
|
|
273
|
+
https://github.com/anysiteio/agent-skills/issues
|
|
274
|
+
|
|
275
|
+
## Support
|
|
276
|
+
|
|
277
|
+
- **GitHub Issues**: [github.com/anysiteio/agent-skills/issues](https://github.com/anysiteio/agent-skills/issues)
|
|
278
|
+
- **Documentation**: [SKILL.md](SKILL.md)
|
|
279
|
+
- **Anysite MCP**: [docs.anysite.io](https://docs.anysite.io)
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT License - see [LICENSE](../../LICENSE) file for details
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
**Note**: This is a security/defensive tool and operates independently from the anysite MCP server. It does not require anysite MCP server to function.
|