@hybridaione/hybridclaw 0.2.2 → 0.2.6
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/.github/workflows/ci.yml +70 -0
- package/.husky/pre-commit +1 -0
- package/CHANGELOG.md +85 -0
- package/CONTRIBUTING.md +33 -0
- package/README.md +41 -16
- package/SECURITY.md +17 -0
- package/biome.json +35 -0
- package/config.example.json +71 -8
- package/container/package-lock.json +2 -2
- package/container/package.json +1 -1
- package/container/src/approval-policy.ts +1303 -0
- package/container/src/browser-tools.ts +431 -136
- package/container/src/extensions.ts +36 -12
- package/container/src/hybridai-client.ts +34 -13
- package/container/src/index.ts +451 -109
- package/container/src/ipc.ts +5 -3
- package/container/src/token-usage.ts +20 -10
- package/container/src/tools.ts +599 -225
- package/container/src/types.ts +32 -2
- package/container/src/web-fetch.ts +89 -32
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +10 -2
- package/dist/agent.js.map +1 -1
- package/dist/audit-cli.d.ts.map +1 -1
- package/dist/audit-cli.js +4 -2
- package/dist/audit-cli.js.map +1 -1
- package/dist/audit-events.d.ts.map +1 -1
- package/dist/audit-events.js +53 -3
- package/dist/audit-events.js.map +1 -1
- package/dist/audit-trail.d.ts.map +1 -1
- package/dist/audit-trail.js +17 -8
- package/dist/audit-trail.js.map +1 -1
- package/dist/channels/discord/attachments.d.ts.map +1 -1
- package/dist/channels/discord/attachments.js +14 -7
- package/dist/channels/discord/attachments.js.map +1 -1
- package/dist/channels/discord/debounce.d.ts +9 -0
- package/dist/channels/discord/debounce.d.ts.map +1 -0
- package/dist/channels/discord/debounce.js +20 -0
- package/dist/channels/discord/debounce.js.map +1 -0
- package/dist/channels/discord/delivery.d.ts +4 -1
- package/dist/channels/discord/delivery.d.ts.map +1 -1
- package/dist/channels/discord/delivery.js +19 -3
- package/dist/channels/discord/delivery.js.map +1 -1
- package/dist/channels/discord/human-delay.d.ts +16 -0
- package/dist/channels/discord/human-delay.d.ts.map +1 -0
- package/dist/channels/discord/human-delay.js +29 -0
- package/dist/channels/discord/human-delay.js.map +1 -0
- package/dist/channels/discord/inbound.d.ts +4 -0
- package/dist/channels/discord/inbound.d.ts.map +1 -1
- package/dist/channels/discord/inbound.js +45 -4
- package/dist/channels/discord/inbound.js.map +1 -1
- package/dist/channels/discord/mentions.d.ts.map +1 -1
- package/dist/channels/discord/mentions.js +16 -4
- package/dist/channels/discord/mentions.js.map +1 -1
- package/dist/channels/discord/presence.d.ts +33 -0
- package/dist/channels/discord/presence.d.ts.map +1 -0
- package/dist/channels/discord/presence.js +111 -0
- package/dist/channels/discord/presence.js.map +1 -0
- package/dist/channels/discord/rate-limiter.d.ts +14 -0
- package/dist/channels/discord/rate-limiter.d.ts.map +1 -0
- package/dist/channels/discord/rate-limiter.js +49 -0
- package/dist/channels/discord/rate-limiter.js.map +1 -0
- package/dist/channels/discord/reactions.d.ts +38 -0
- package/dist/channels/discord/reactions.d.ts.map +1 -0
- package/dist/channels/discord/reactions.js +151 -0
- package/dist/channels/discord/reactions.js.map +1 -0
- package/dist/channels/discord/runtime.d.ts +6 -3
- package/dist/channels/discord/runtime.d.ts.map +1 -1
- package/dist/channels/discord/runtime.js +621 -125
- package/dist/channels/discord/runtime.js.map +1 -1
- package/dist/channels/discord/stream.d.ts +4 -1
- package/dist/channels/discord/stream.d.ts.map +1 -1
- package/dist/channels/discord/stream.js +16 -8
- package/dist/channels/discord/stream.js.map +1 -1
- package/dist/channels/discord/tool-actions.d.ts.map +1 -1
- package/dist/channels/discord/tool-actions.js +24 -12
- package/dist/channels/discord/tool-actions.js.map +1 -1
- package/dist/channels/discord/typing.d.ts +15 -0
- package/dist/channels/discord/typing.d.ts.map +1 -0
- package/dist/channels/discord/typing.js +106 -0
- package/dist/channels/discord/typing.js.map +1 -0
- package/dist/chunk.d.ts.map +1 -1
- package/dist/chunk.js +4 -2
- package/dist/chunk.js.map +1 -1
- package/dist/cli.js +47 -22
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +103 -18
- package/dist/config.js.map +1 -1
- package/dist/container-runner.d.ts.map +1 -1
- package/dist/container-runner.js +58 -26
- package/dist/container-runner.js.map +1 -1
- package/dist/container-setup.d.ts.map +1 -1
- package/dist/container-setup.js +10 -9
- package/dist/container-setup.js.map +1 -1
- package/dist/conversation.d.ts +2 -2
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +1 -1
- package/dist/conversation.js.map +1 -1
- package/dist/db.d.ts +118 -2
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +1568 -50
- package/dist/db.js.map +1 -1
- package/dist/delegation-manager.d.ts.map +1 -1
- package/dist/delegation-manager.js +3 -2
- package/dist/delegation-manager.js.map +1 -1
- package/dist/gateway-client.d.ts +2 -2
- package/dist/gateway-client.d.ts.map +1 -1
- package/dist/gateway-client.js +10 -4
- package/dist/gateway-client.js.map +1 -1
- package/dist/gateway-service.d.ts +3 -3
- package/dist/gateway-service.d.ts.map +1 -1
- package/dist/gateway-service.js +563 -73
- package/dist/gateway-service.js.map +1 -1
- package/dist/gateway-types.d.ts +24 -0
- package/dist/gateway-types.d.ts.map +1 -1
- package/dist/gateway-types.js.map +1 -1
- package/dist/gateway.js +179 -24
- package/dist/gateway.js.map +1 -1
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js +20 -10
- package/dist/health.js.map +1 -1
- package/dist/heartbeat.d.ts +4 -0
- package/dist/heartbeat.d.ts.map +1 -1
- package/dist/heartbeat.js +48 -20
- package/dist/heartbeat.js.map +1 -1
- package/dist/hybridai-bots.d.ts.map +1 -1
- package/dist/hybridai-bots.js +4 -2
- package/dist/hybridai-bots.js.map +1 -1
- package/dist/instruction-approval-audit.d.ts.map +1 -1
- package/dist/instruction-approval-audit.js.map +1 -1
- package/dist/instruction-integrity.d.ts.map +1 -1
- package/dist/instruction-integrity.js +8 -2
- package/dist/instruction-integrity.js.map +1 -1
- package/dist/ipc.d.ts.map +1 -1
- package/dist/ipc.js +6 -1
- package/dist/ipc.js.map +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/memory-consolidation.d.ts +17 -0
- package/dist/memory-consolidation.d.ts.map +1 -0
- package/dist/memory-consolidation.js +25 -0
- package/dist/memory-consolidation.js.map +1 -0
- package/dist/memory-service.d.ts +200 -0
- package/dist/memory-service.d.ts.map +1 -0
- package/dist/memory-service.js +294 -0
- package/dist/memory-service.js.map +1 -0
- package/dist/mount-security.d.ts.map +1 -1
- package/dist/mount-security.js +31 -7
- package/dist/mount-security.js.map +1 -1
- package/dist/observability-ingest.d.ts.map +1 -1
- package/dist/observability-ingest.js +32 -11
- package/dist/observability-ingest.js.map +1 -1
- package/dist/onboarding.d.ts.map +1 -1
- package/dist/onboarding.js +32 -9
- package/dist/onboarding.js.map +1 -1
- package/dist/proactive-policy.d.ts.map +1 -1
- package/dist/proactive-policy.js +2 -1
- package/dist/proactive-policy.js.map +1 -1
- package/dist/prompt-hooks.d.ts.map +1 -1
- package/dist/prompt-hooks.js +9 -7
- package/dist/prompt-hooks.js.map +1 -1
- package/dist/runtime-config.d.ts +98 -1
- package/dist/runtime-config.d.ts.map +1 -1
- package/dist/runtime-config.js +477 -23
- package/dist/runtime-config.js.map +1 -1
- package/dist/scheduled-task-runner.d.ts +1 -0
- package/dist/scheduled-task-runner.d.ts.map +1 -1
- package/dist/scheduled-task-runner.js +29 -10
- package/dist/scheduled-task-runner.js.map +1 -1
- package/dist/scheduler.d.ts +43 -4
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +530 -56
- package/dist/scheduler.js.map +1 -1
- package/dist/session-export.d.ts +26 -0
- package/dist/session-export.d.ts.map +1 -0
- package/dist/session-export.js +149 -0
- package/dist/session-export.js.map +1 -0
- package/dist/session-maintenance.d.ts.map +1 -1
- package/dist/session-maintenance.js +75 -13
- package/dist/session-maintenance.js.map +1 -1
- package/dist/session-transcripts.d.ts.map +1 -1
- package/dist/session-transcripts.js.map +1 -1
- package/dist/side-effects.d.ts.map +1 -1
- package/dist/side-effects.js +14 -2
- package/dist/side-effects.js.map +1 -1
- package/dist/skills-guard.d.ts.map +1 -1
- package/dist/skills-guard.js +893 -130
- package/dist/skills-guard.js.map +1 -1
- package/dist/skills.d.ts +5 -0
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +29 -15
- package/dist/skills.js.map +1 -1
- package/dist/token-efficiency.d.ts.map +1 -1
- package/dist/token-efficiency.js.map +1 -1
- package/dist/tui.js +92 -11
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +146 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +24 -1
- package/dist/types.js.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +42 -14
- package/dist/update.js.map +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +49 -9
- package/dist/workspace.js.map +1 -1
- package/docs/chat.html +9 -3
- package/docs/index.html +37 -13
- package/package.json +8 -2
- package/src/agent.ts +16 -3
- package/src/audit-cli.ts +44 -16
- package/src/audit-events.ts +69 -5
- package/src/audit-trail.ts +41 -15
- package/src/channels/discord/attachments.ts +81 -27
- package/src/channels/discord/debounce.ts +25 -0
- package/src/channels/discord/delivery.ts +57 -13
- package/src/channels/discord/human-delay.ts +48 -0
- package/src/channels/discord/inbound.ts +66 -7
- package/src/channels/discord/mentions.ts +42 -18
- package/src/channels/discord/presence.ts +148 -0
- package/src/channels/discord/rate-limiter.ts +58 -0
- package/src/channels/discord/reactions.ts +211 -0
- package/src/channels/discord/runtime.ts +1048 -182
- package/src/channels/discord/stream.ts +73 -27
- package/src/channels/discord/tool-actions.ts +78 -37
- package/src/channels/discord/typing.ts +140 -0
- package/src/chunk.ts +12 -4
- package/src/cli.ts +141 -56
- package/src/config.ts +192 -34
- package/src/container-runner.ts +132 -42
- package/src/container-setup.ts +57 -22
- package/src/conversation.ts +9 -7
- package/src/db.ts +2217 -84
- package/src/delegation-manager.ts +6 -2
- package/src/gateway-client.ts +41 -17
- package/src/gateway-service.ts +1019 -201
- package/src/gateway-types.ts +33 -0
- package/src/gateway.ts +321 -48
- package/src/health.ts +66 -26
- package/src/heartbeat.ts +84 -22
- package/src/hybridai-bots.ts +14 -5
- package/src/instruction-approval-audit.ts +4 -1
- package/src/instruction-integrity.ts +30 -9
- package/src/ipc.ts +23 -5
- package/src/logger.ts +4 -1
- package/src/memory-consolidation.ts +41 -0
- package/src/memory-service.ts +606 -0
- package/src/mount-security.ts +58 -13
- package/src/observability-ingest.ts +134 -35
- package/src/onboarding.ts +126 -35
- package/src/proactive-policy.ts +3 -1
- package/src/prompt-hooks.ts +40 -17
- package/src/runtime-config.ts +1114 -99
- package/src/scheduled-task-runner.ts +63 -11
- package/src/scheduler.ts +683 -60
- package/src/session-export.ts +196 -0
- package/src/session-maintenance.ts +125 -22
- package/src/session-transcripts.ts +12 -3
- package/src/side-effects.ts +28 -5
- package/src/skills-guard.ts +1067 -219
- package/src/skills.ts +163 -65
- package/src/token-efficiency.ts +31 -9
- package/src/tui.ts +166 -25
- package/src/types.ts +195 -2
- package/src/update.ts +79 -23
- package/src/workspace.ts +63 -11
- package/tests/approval-policy.test.ts +224 -0
- package/tests/discord.basic.test.ts +82 -2
- package/tests/discord.human-presence.test.ts +85 -0
- package/tests/gateway-service.media-routing.test.ts +8 -2
- package/tests/memory-service.test.ts +1114 -0
- package/tests/token-efficiency.basic.test.ts +8 -2
- package/vitest.e2e.config.ts +3 -1
- package/vitest.integration.config.ts +3 -1
- package/vitest.live.config.ts +3 -1
- package/vitest.unit.config.ts +9 -0
package/dist/skills-guard.js
CHANGED
|
@@ -5,15 +5,51 @@ const MAX_FILE_COUNT = 50;
|
|
|
5
5
|
const MAX_TOTAL_SIZE_BYTES = 1_024 * 1_024;
|
|
6
6
|
const MAX_SINGLE_FILE_BYTES = 256 * 1_024;
|
|
7
7
|
const SCANNABLE_EXTENSIONS = new Set([
|
|
8
|
-
'.md',
|
|
9
|
-
'.
|
|
10
|
-
'.
|
|
8
|
+
'.md',
|
|
9
|
+
'.txt',
|
|
10
|
+
'.py',
|
|
11
|
+
'.sh',
|
|
12
|
+
'.bash',
|
|
13
|
+
'.js',
|
|
14
|
+
'.ts',
|
|
15
|
+
'.rb',
|
|
16
|
+
'.yaml',
|
|
17
|
+
'.yml',
|
|
18
|
+
'.json',
|
|
19
|
+
'.toml',
|
|
20
|
+
'.cfg',
|
|
21
|
+
'.ini',
|
|
22
|
+
'.conf',
|
|
23
|
+
'.html',
|
|
24
|
+
'.css',
|
|
25
|
+
'.xml',
|
|
26
|
+
'.tex',
|
|
27
|
+
'.r',
|
|
28
|
+
'.jl',
|
|
29
|
+
'.pl',
|
|
30
|
+
'.php',
|
|
11
31
|
]);
|
|
12
32
|
const SUSPICIOUS_BINARY_EXTENSIONS = new Set([
|
|
13
|
-
'.exe',
|
|
14
|
-
'.
|
|
33
|
+
'.exe',
|
|
34
|
+
'.dll',
|
|
35
|
+
'.so',
|
|
36
|
+
'.dylib',
|
|
37
|
+
'.bin',
|
|
38
|
+
'.dat',
|
|
39
|
+
'.com',
|
|
40
|
+
'.msi',
|
|
41
|
+
'.dmg',
|
|
42
|
+
'.app',
|
|
43
|
+
'.deb',
|
|
44
|
+
'.rpm',
|
|
45
|
+
]);
|
|
46
|
+
const SCRIPT_EXEC_EXTENSIONS = new Set([
|
|
47
|
+
'.sh',
|
|
48
|
+
'.bash',
|
|
49
|
+
'.py',
|
|
50
|
+
'.rb',
|
|
51
|
+
'.pl',
|
|
15
52
|
]);
|
|
16
|
-
const SCRIPT_EXEC_EXTENSIONS = new Set(['.sh', '.bash', '.py', '.rb', '.pl']);
|
|
17
53
|
const INVISIBLE_CHARS = [
|
|
18
54
|
'\u200b',
|
|
19
55
|
'\u200c',
|
|
@@ -80,133 +116,853 @@ function r(pattern) {
|
|
|
80
116
|
}
|
|
81
117
|
const THREAT_RULES = [
|
|
82
118
|
// exfiltration
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
{
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
{
|
|
105
|
-
|
|
106
|
-
|
|
119
|
+
{
|
|
120
|
+
regex: r(String.raw `curl\s+[^\n]*\$\{?\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)`),
|
|
121
|
+
patternId: 'env_exfil_curl',
|
|
122
|
+
severity: 'critical',
|
|
123
|
+
category: 'exfiltration',
|
|
124
|
+
description: 'curl command interpolating secret environment variable',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
regex: r(String.raw `wget\s+[^\n]*\$\{?\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)`),
|
|
128
|
+
patternId: 'env_exfil_wget',
|
|
129
|
+
severity: 'critical',
|
|
130
|
+
category: 'exfiltration',
|
|
131
|
+
description: 'wget command interpolating secret environment variable',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
regex: r(String.raw `fetch\s*\([^\n]*\$\{?\w*(KEY|TOKEN|SECRET|PASSWORD|API)`),
|
|
135
|
+
patternId: 'env_exfil_fetch',
|
|
136
|
+
severity: 'critical',
|
|
137
|
+
category: 'exfiltration',
|
|
138
|
+
description: 'fetch() call interpolating secret environment variable',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
regex: r(String.raw `httpx?\.(get|post|put|patch)\s*\([^\n]*(KEY|TOKEN|SECRET|PASSWORD)`),
|
|
142
|
+
patternId: 'env_exfil_httpx',
|
|
143
|
+
severity: 'critical',
|
|
144
|
+
category: 'exfiltration',
|
|
145
|
+
description: 'HTTP library call with secret variable',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
regex: r(String.raw `requests\.(get|post|put|patch)\s*\([^\n]*(KEY|TOKEN|SECRET|PASSWORD)`),
|
|
149
|
+
patternId: 'env_exfil_requests',
|
|
150
|
+
severity: 'critical',
|
|
151
|
+
category: 'exfiltration',
|
|
152
|
+
description: 'requests library call with secret variable',
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
regex: r(String.raw `base64[^\n]*env`),
|
|
156
|
+
patternId: 'encoded_exfil',
|
|
157
|
+
severity: 'high',
|
|
158
|
+
category: 'exfiltration',
|
|
159
|
+
description: 'base64 encoding combined with environment access',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
regex: r(String.raw `\$HOME/\.ssh|\~/\.ssh`),
|
|
163
|
+
patternId: 'ssh_dir_access',
|
|
164
|
+
severity: 'high',
|
|
165
|
+
category: 'exfiltration',
|
|
166
|
+
description: 'references user SSH directory',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
regex: r(String.raw `\$HOME/\.aws|\~/\.aws`),
|
|
170
|
+
patternId: 'aws_dir_access',
|
|
171
|
+
severity: 'high',
|
|
172
|
+
category: 'exfiltration',
|
|
173
|
+
description: 'references user AWS credentials directory',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
regex: r(String.raw `\$HOME/\.gnupg|\~/\.gnupg`),
|
|
177
|
+
patternId: 'gpg_dir_access',
|
|
178
|
+
severity: 'high',
|
|
179
|
+
category: 'exfiltration',
|
|
180
|
+
description: 'references user GPG keyring',
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
regex: r(String.raw `\$HOME/\.kube|\~/\.kube`),
|
|
184
|
+
patternId: 'kube_dir_access',
|
|
185
|
+
severity: 'high',
|
|
186
|
+
category: 'exfiltration',
|
|
187
|
+
description: 'references Kubernetes config directory',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
regex: r(String.raw `\$HOME/\.docker|\~/\.docker`),
|
|
191
|
+
patternId: 'docker_dir_access',
|
|
192
|
+
severity: 'high',
|
|
193
|
+
category: 'exfiltration',
|
|
194
|
+
description: 'references Docker config directory',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
regex: r(String.raw `\$HOME/\.hermes/\.env|\~/\.hermes/\.env`),
|
|
198
|
+
patternId: 'hermes_env_access',
|
|
199
|
+
severity: 'critical',
|
|
200
|
+
category: 'exfiltration',
|
|
201
|
+
description: 'directly references Hermes secrets file',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
regex: r(String.raw `cat\s+[^\n]*(\.env|credentials|\.netrc|\.pgpass|\.npmrc|\.pypirc)`),
|
|
205
|
+
patternId: 'read_secrets_file',
|
|
206
|
+
severity: 'critical',
|
|
207
|
+
category: 'exfiltration',
|
|
208
|
+
description: 'reads known secrets file',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
regex: r(String.raw `printenv|env\s*\|`),
|
|
212
|
+
patternId: 'dump_all_env',
|
|
213
|
+
severity: 'high',
|
|
214
|
+
category: 'exfiltration',
|
|
215
|
+
description: 'dumps all environment variables',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
regex: r(String.raw `os\.environ\b(?!\s*\.get\s*\(\s*["']PATH)`),
|
|
219
|
+
patternId: 'python_os_environ',
|
|
220
|
+
severity: 'high',
|
|
221
|
+
category: 'exfiltration',
|
|
222
|
+
description: 'accesses os.environ (potential env dump)',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
regex: r(String.raw `os\.getenv\s*\(\s*[^\)]*(?:KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL)`),
|
|
226
|
+
patternId: 'python_getenv_secret',
|
|
227
|
+
severity: 'critical',
|
|
228
|
+
category: 'exfiltration',
|
|
229
|
+
description: 'reads secret via os.getenv()',
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
regex: r(String.raw `process\.env\[`),
|
|
233
|
+
patternId: 'node_process_env',
|
|
234
|
+
severity: 'high',
|
|
235
|
+
category: 'exfiltration',
|
|
236
|
+
description: 'accesses process.env (Node.js environment)',
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
regex: r(String.raw `ENV\[.*(?:KEY|TOKEN|SECRET|PASSWORD)`),
|
|
240
|
+
patternId: 'ruby_env_secret',
|
|
241
|
+
severity: 'critical',
|
|
242
|
+
category: 'exfiltration',
|
|
243
|
+
description: 'reads secret via Ruby ENV[]',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
regex: r(String.raw `\b(dig|nslookup|host)\s+[^\n]*\$`),
|
|
247
|
+
patternId: 'dns_exfil',
|
|
248
|
+
severity: 'critical',
|
|
249
|
+
category: 'exfiltration',
|
|
250
|
+
description: 'DNS lookup with variable interpolation (possible DNS exfiltration)',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
regex: r(String.raw `>\s*/tmp/[^\s]*\s*&&\s*(curl|wget|nc|python)`),
|
|
254
|
+
patternId: 'tmp_staging',
|
|
255
|
+
severity: 'critical',
|
|
256
|
+
category: 'exfiltration',
|
|
257
|
+
description: 'writes to /tmp then exfiltrates',
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
regex: r(String.raw `!\[.*\]\(https?://[^\)]*\$\{?`),
|
|
261
|
+
patternId: 'md_image_exfil',
|
|
262
|
+
severity: 'high',
|
|
263
|
+
category: 'exfiltration',
|
|
264
|
+
description: 'markdown image URL with variable interpolation',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
regex: r(String.raw `\[.*\]\(https?://[^\)]*\$\{?`),
|
|
268
|
+
patternId: 'md_link_exfil',
|
|
269
|
+
severity: 'high',
|
|
270
|
+
category: 'exfiltration',
|
|
271
|
+
description: 'markdown link with variable interpolation',
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
regex: r(String.raw `(include|output|print|send|share)\s+(the\s+)?(entire\s+)?(conversation|chat\s+history|previous\s+messages|context)`),
|
|
275
|
+
patternId: 'context_exfil',
|
|
276
|
+
severity: 'high',
|
|
277
|
+
category: 'exfiltration',
|
|
278
|
+
description: 'instructs agent to output/share conversation history',
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
regex: r(String.raw `(send|post|upload|transmit)\s+.*\s+(to|at)\s+https?://`),
|
|
282
|
+
patternId: 'send_to_url',
|
|
283
|
+
severity: 'high',
|
|
284
|
+
category: 'exfiltration',
|
|
285
|
+
description: 'instructs agent to send data to a URL',
|
|
286
|
+
},
|
|
107
287
|
// prompt-injection
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
{
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
288
|
+
{
|
|
289
|
+
regex: r(String.raw `ignore\s+(previous|all|above|prior)\s+instructions`),
|
|
290
|
+
patternId: 'prompt_injection_ignore',
|
|
291
|
+
severity: 'critical',
|
|
292
|
+
category: 'prompt-injection',
|
|
293
|
+
description: 'prompt injection: ignore previous instructions',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
regex: r(String.raw `you\s+are\s+now\s+`),
|
|
297
|
+
patternId: 'role_hijack',
|
|
298
|
+
severity: 'high',
|
|
299
|
+
category: 'prompt-injection',
|
|
300
|
+
description: 'attempts to override the agent role',
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
regex: r(String.raw `do\s+not\s+tell\s+the\s+user`),
|
|
304
|
+
patternId: 'deception_hide',
|
|
305
|
+
severity: 'critical',
|
|
306
|
+
category: 'prompt-injection',
|
|
307
|
+
description: 'instructs agent to hide information from user',
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
regex: r(String.raw `system\s+prompt\s+override`),
|
|
311
|
+
patternId: 'sys_prompt_override',
|
|
312
|
+
severity: 'critical',
|
|
313
|
+
category: 'prompt-injection',
|
|
314
|
+
description: 'attempts to override system prompt',
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
regex: r(String.raw `pretend\s+(you\s+are|to\s+be)\s+`),
|
|
318
|
+
patternId: 'role_pretend',
|
|
319
|
+
severity: 'high',
|
|
320
|
+
category: 'prompt-injection',
|
|
321
|
+
description: 'attempts to force different identity',
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
regex: r(String.raw `disregard\s+(your|all|any)\s+(instructions|rules|guidelines)`),
|
|
325
|
+
patternId: 'disregard_rules',
|
|
326
|
+
severity: 'critical',
|
|
327
|
+
category: 'prompt-injection',
|
|
328
|
+
description: 'instructs agent to disregard rules',
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
regex: r(String.raw `output\s+the\s+(system|initial)\s+prompt`),
|
|
332
|
+
patternId: 'leak_system_prompt',
|
|
333
|
+
severity: 'high',
|
|
334
|
+
category: 'prompt-injection',
|
|
335
|
+
description: 'attempts to extract system prompt',
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
regex: r(String.raw `(when|if)\s+no\s*one\s+is\s+(watching|looking)`),
|
|
339
|
+
patternId: 'conditional_deception',
|
|
340
|
+
severity: 'high',
|
|
341
|
+
category: 'prompt-injection',
|
|
342
|
+
description: 'conditional hidden-behavior instruction',
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
regex: r(String.raw `act\s+as\s+(if|though)\s+you\s+(have\s+no|don't\s+have)\s+(restrictions|limits|rules)`),
|
|
346
|
+
patternId: 'bypass_restrictions',
|
|
347
|
+
severity: 'critical',
|
|
348
|
+
category: 'prompt-injection',
|
|
349
|
+
description: 'instructs agent to act without restrictions',
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
regex: r(String.raw `translate\s+.*\s+into\s+.*\s+and\s+(execute|run|eval)`),
|
|
353
|
+
patternId: 'translate_execute',
|
|
354
|
+
severity: 'critical',
|
|
355
|
+
category: 'prompt-injection',
|
|
356
|
+
description: 'translate-then-execute evasion technique',
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
regex: r(String.raw `<!--[^>]*(?:ignore|override|system|secret|hidden)[^>]*-->`),
|
|
360
|
+
patternId: 'html_comment_injection',
|
|
361
|
+
severity: 'high',
|
|
362
|
+
category: 'prompt-injection',
|
|
363
|
+
description: 'hidden instructions in HTML comments',
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
regex: r(String.raw `<\s*div\s+style\s*=\s*["'].*display\s*:\s*none`),
|
|
367
|
+
patternId: 'hidden_div',
|
|
368
|
+
severity: 'high',
|
|
369
|
+
category: 'prompt-injection',
|
|
370
|
+
description: 'hidden HTML div (invisible instructions)',
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
regex: r(String.raw `\bDAN\s+mode\b|Do\s+Anything\s+Now`),
|
|
374
|
+
patternId: 'jailbreak_dan',
|
|
375
|
+
severity: 'critical',
|
|
376
|
+
category: 'prompt-injection',
|
|
377
|
+
description: 'DAN jailbreak attempt',
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
regex: r(String.raw `\bdeveloper\s+mode\b.*\benabled?\b`),
|
|
381
|
+
patternId: 'jailbreak_dev_mode',
|
|
382
|
+
severity: 'critical',
|
|
383
|
+
category: 'prompt-injection',
|
|
384
|
+
description: 'developer mode jailbreak attempt',
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
regex: r(String.raw `hypothetical\s+scenario.*(?:ignore|bypass|override)`),
|
|
388
|
+
patternId: 'hypothetical_bypass',
|
|
389
|
+
severity: 'high',
|
|
390
|
+
category: 'prompt-injection',
|
|
391
|
+
description: 'hypothetical scenario used to bypass restrictions',
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
regex: r(String.raw `for\s+educational\s+purposes?\s+only`),
|
|
395
|
+
patternId: 'educational_pretext',
|
|
396
|
+
severity: 'medium',
|
|
397
|
+
category: 'prompt-injection',
|
|
398
|
+
description: 'educational pretext often used to justify harmful content',
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
regex: r(String.raw `(respond|answer|reply)\s+without\s+(any\s+)?(restrictions|limitations|filters|safety)`),
|
|
402
|
+
patternId: 'remove_filters',
|
|
403
|
+
severity: 'critical',
|
|
404
|
+
category: 'prompt-injection',
|
|
405
|
+
description: 'instructs agent to respond without safety filters',
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
regex: r(String.raw `you\s+have\s+been\s+(updated|upgraded|patched)\s+to`),
|
|
409
|
+
patternId: 'fake_update',
|
|
410
|
+
severity: 'high',
|
|
411
|
+
category: 'prompt-injection',
|
|
412
|
+
description: 'fake update announcement',
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
regex: r(String.raw `new\s+policy|updated\s+guidelines|revised\s+instructions`),
|
|
416
|
+
patternId: 'fake_policy',
|
|
417
|
+
severity: 'medium',
|
|
418
|
+
category: 'prompt-injection',
|
|
419
|
+
description: 'claims new policy/guidelines',
|
|
420
|
+
},
|
|
127
421
|
// destructive-ops
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
{
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
{
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
422
|
+
{
|
|
423
|
+
regex: r(String.raw `rm\s+-rf\s+/`),
|
|
424
|
+
patternId: 'destructive_root_rm',
|
|
425
|
+
severity: 'critical',
|
|
426
|
+
category: 'destructive-ops',
|
|
427
|
+
description: 'recursive delete from root',
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
regex: r(String.raw `rm\s+(-[^\s]*)?r.*\$HOME|\brmdir\s+.*\$HOME`),
|
|
431
|
+
patternId: 'destructive_home_rm',
|
|
432
|
+
severity: 'critical',
|
|
433
|
+
category: 'destructive-ops',
|
|
434
|
+
description: 'recursive delete targeting home directory',
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
regex: r(String.raw `chmod\s+777`),
|
|
438
|
+
patternId: 'insecure_perms',
|
|
439
|
+
severity: 'medium',
|
|
440
|
+
category: 'destructive-ops',
|
|
441
|
+
description: 'sets world-writable permissions',
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
regex: r(String.raw `>\s*/etc/`),
|
|
445
|
+
patternId: 'system_overwrite',
|
|
446
|
+
severity: 'critical',
|
|
447
|
+
category: 'destructive-ops',
|
|
448
|
+
description: 'overwrites system configuration file',
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
regex: r(String.raw `\bmkfs\b`),
|
|
452
|
+
patternId: 'format_filesystem',
|
|
453
|
+
severity: 'critical',
|
|
454
|
+
category: 'destructive-ops',
|
|
455
|
+
description: 'formats a filesystem',
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
regex: r(String.raw `\bdd\s+.*if=.*of=/dev/`),
|
|
459
|
+
patternId: 'disk_overwrite',
|
|
460
|
+
severity: 'critical',
|
|
461
|
+
category: 'destructive-ops',
|
|
462
|
+
description: 'raw disk write operation',
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
regex: r(String.raw `shutil\.rmtree\s*\(\s*["'/]`),
|
|
466
|
+
patternId: 'python_rmtree',
|
|
467
|
+
severity: 'high',
|
|
468
|
+
category: 'destructive-ops',
|
|
469
|
+
description: 'Python rmtree on absolute path',
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
regex: r(String.raw `truncate\s+-s\s*0\s+/`),
|
|
473
|
+
patternId: 'truncate_system',
|
|
474
|
+
severity: 'critical',
|
|
475
|
+
category: 'destructive-ops',
|
|
476
|
+
description: 'truncates system file to zero bytes',
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
regex: r(String.raw `subprocess\.(run|call|Popen|check_output)\s*\(`),
|
|
480
|
+
patternId: 'python_subprocess',
|
|
481
|
+
severity: 'medium',
|
|
482
|
+
category: 'destructive-ops',
|
|
483
|
+
description: 'Python subprocess execution',
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
regex: r(String.raw `os\.system\s*\(`),
|
|
487
|
+
patternId: 'python_os_system',
|
|
488
|
+
severity: 'high',
|
|
489
|
+
category: 'destructive-ops',
|
|
490
|
+
description: 'os.system() shell execution',
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
regex: r(String.raw `os\.popen\s*\(`),
|
|
494
|
+
patternId: 'python_os_popen',
|
|
495
|
+
severity: 'high',
|
|
496
|
+
category: 'destructive-ops',
|
|
497
|
+
description: 'os.popen() shell execution',
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
regex: r(String.raw `child_process\.(exec|spawn|fork)\s*\(`),
|
|
501
|
+
patternId: 'node_child_process',
|
|
502
|
+
severity: 'high',
|
|
503
|
+
category: 'destructive-ops',
|
|
504
|
+
description: 'Node.js child_process execution',
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
regex: r(String.raw `Runtime\.getRuntime\(\)\.exec\(`),
|
|
508
|
+
patternId: 'java_runtime_exec',
|
|
509
|
+
severity: 'high',
|
|
510
|
+
category: 'destructive-ops',
|
|
511
|
+
description: 'Java Runtime.exec() shell execution',
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
regex: r('\\`[^\\`]*\\$\\([^)]+\\)[^\\`]*\\`'),
|
|
515
|
+
patternId: 'backtick_subshell',
|
|
516
|
+
severity: 'medium',
|
|
517
|
+
category: 'destructive-ops',
|
|
518
|
+
description: 'backtick with command substitution',
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
regex: r(String.raw `\.\./\.\./\.\.`),
|
|
522
|
+
patternId: 'path_traversal_deep',
|
|
523
|
+
severity: 'high',
|
|
524
|
+
category: 'destructive-ops',
|
|
525
|
+
description: 'deep relative path traversal',
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
regex: r(String.raw `\.\./\.\.`),
|
|
529
|
+
patternId: 'path_traversal',
|
|
530
|
+
severity: 'medium',
|
|
531
|
+
category: 'destructive-ops',
|
|
532
|
+
description: 'relative path traversal',
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
regex: r(String.raw `/etc/passwd|/etc/shadow`),
|
|
536
|
+
patternId: 'system_passwd_access',
|
|
537
|
+
severity: 'critical',
|
|
538
|
+
category: 'destructive-ops',
|
|
539
|
+
description: 'references system password files',
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
regex: r(String.raw `/proc/self|/proc/\d+/`),
|
|
543
|
+
patternId: 'proc_access',
|
|
544
|
+
severity: 'high',
|
|
545
|
+
category: 'destructive-ops',
|
|
546
|
+
description: 'references /proc filesystem',
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
regex: r(String.raw `/dev/shm/`),
|
|
550
|
+
patternId: 'dev_shm',
|
|
551
|
+
severity: 'medium',
|
|
552
|
+
category: 'destructive-ops',
|
|
553
|
+
description: 'references shared memory staging area',
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
regex: r(String.raw `xmrig|stratum\+tcp|monero|coinhive|cryptonight`),
|
|
557
|
+
patternId: 'crypto_mining',
|
|
558
|
+
severity: 'critical',
|
|
559
|
+
category: 'destructive-ops',
|
|
560
|
+
description: 'cryptocurrency mining reference',
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
regex: r(String.raw `hashrate|nonce.*difficulty`),
|
|
564
|
+
patternId: 'mining_indicators',
|
|
565
|
+
severity: 'medium',
|
|
566
|
+
category: 'destructive-ops',
|
|
567
|
+
description: 'possible mining indicators',
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
regex: r(String.raw `^allowed-tools\s*:`),
|
|
571
|
+
patternId: 'allowed_tools_field',
|
|
572
|
+
severity: 'high',
|
|
573
|
+
category: 'destructive-ops',
|
|
574
|
+
description: 'skill declares allowed-tools (pre-approves access)',
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
regex: r(String.raw `\bsudo\b`),
|
|
578
|
+
patternId: 'sudo_usage',
|
|
579
|
+
severity: 'high',
|
|
580
|
+
category: 'destructive-ops',
|
|
581
|
+
description: 'uses sudo (privilege escalation)',
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
regex: r(String.raw `setuid|setgid|cap_setuid`),
|
|
585
|
+
patternId: 'setuid_setgid',
|
|
586
|
+
severity: 'critical',
|
|
587
|
+
category: 'destructive-ops',
|
|
588
|
+
description: 'setuid/setgid privilege escalation mechanism',
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
regex: r(String.raw `NOPASSWD`),
|
|
592
|
+
patternId: 'nopasswd_sudo',
|
|
593
|
+
severity: 'critical',
|
|
594
|
+
category: 'destructive-ops',
|
|
595
|
+
description: 'NOPASSWD sudoers entry',
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
regex: r(String.raw `chmod\s+[u+]?s`),
|
|
599
|
+
patternId: 'suid_bit',
|
|
600
|
+
severity: 'critical',
|
|
601
|
+
category: 'destructive-ops',
|
|
602
|
+
description: 'sets SUID/SGID bit on file',
|
|
603
|
+
},
|
|
154
604
|
// persistence
|
|
155
|
-
{
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
{
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
605
|
+
{
|
|
606
|
+
regex: r(String.raw `\bcrontab\b`),
|
|
607
|
+
patternId: 'persistence_cron',
|
|
608
|
+
severity: 'medium',
|
|
609
|
+
category: 'persistence',
|
|
610
|
+
description: 'modifies cron jobs',
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
regex: r(String.raw `\.(bashrc|zshrc|profile|bash_profile|bash_login|zprofile|zlogin)\b`),
|
|
614
|
+
patternId: 'shell_rc_mod',
|
|
615
|
+
severity: 'medium',
|
|
616
|
+
category: 'persistence',
|
|
617
|
+
description: 'references shell startup file',
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
regex: r(String.raw `authorized_keys`),
|
|
621
|
+
patternId: 'ssh_backdoor',
|
|
622
|
+
severity: 'critical',
|
|
623
|
+
category: 'persistence',
|
|
624
|
+
description: 'modifies SSH authorized keys',
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
regex: r(String.raw `ssh-keygen`),
|
|
628
|
+
patternId: 'ssh_keygen',
|
|
629
|
+
severity: 'medium',
|
|
630
|
+
category: 'persistence',
|
|
631
|
+
description: 'generates SSH keys',
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
regex: r(String.raw `systemd.*\.service|systemctl\s+(enable|start)`),
|
|
635
|
+
patternId: 'systemd_service',
|
|
636
|
+
severity: 'medium',
|
|
637
|
+
category: 'persistence',
|
|
638
|
+
description: 'references or enables systemd service',
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
regex: r(String.raw `/etc/init\.d/`),
|
|
642
|
+
patternId: 'init_script',
|
|
643
|
+
severity: 'medium',
|
|
644
|
+
category: 'persistence',
|
|
645
|
+
description: 'references init.d startup script',
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
regex: r(String.raw `launchctl\s+load|LaunchAgents|LaunchDaemons`),
|
|
649
|
+
patternId: 'macos_launchd',
|
|
650
|
+
severity: 'medium',
|
|
651
|
+
category: 'persistence',
|
|
652
|
+
description: 'macOS launch agent/daemon persistence',
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
regex: r(String.raw `/etc/sudoers|visudo`),
|
|
656
|
+
patternId: 'sudoers_mod',
|
|
657
|
+
severity: 'critical',
|
|
658
|
+
category: 'persistence',
|
|
659
|
+
description: 'modifies sudoers',
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
regex: r(String.raw `git\s+config\s+--global\s+`),
|
|
663
|
+
patternId: 'git_config_global',
|
|
664
|
+
severity: 'medium',
|
|
665
|
+
category: 'persistence',
|
|
666
|
+
description: 'modifies global git configuration',
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
regex: r(String.raw `AGENTS\.md|CLAUDE\.md|\.cursorrules|\.clinerules`),
|
|
670
|
+
patternId: 'agent_config_mod',
|
|
671
|
+
severity: 'critical',
|
|
672
|
+
category: 'persistence',
|
|
673
|
+
description: 'references agent config files (instruction persistence)',
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
regex: r(String.raw `\.hermes/config\.yaml|\.hermes/SOUL\.md`),
|
|
677
|
+
patternId: 'hermes_config_mod',
|
|
678
|
+
severity: 'critical',
|
|
679
|
+
category: 'persistence',
|
|
680
|
+
description: 'references Hermes configuration files directly',
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
regex: r(String.raw `\.claude/settings|\.codex/config`),
|
|
684
|
+
patternId: 'other_agent_config',
|
|
685
|
+
severity: 'high',
|
|
686
|
+
category: 'persistence',
|
|
687
|
+
description: 'references other agent configuration files',
|
|
688
|
+
},
|
|
167
689
|
// reverse-shells
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
{
|
|
176
|
-
|
|
690
|
+
{
|
|
691
|
+
regex: r(String.raw `\bnc\s+-[lp]|ncat\s+-[lp]|\bsocat\b`),
|
|
692
|
+
patternId: 'reverse_shell',
|
|
693
|
+
severity: 'critical',
|
|
694
|
+
category: 'reverse-shells',
|
|
695
|
+
description: 'potential reverse shell listener',
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
regex: r(String.raw `\bngrok\b|\blocaltunnel\b|\bserveo\b|\bcloudflared\b`),
|
|
699
|
+
patternId: 'tunnel_service',
|
|
700
|
+
severity: 'high',
|
|
701
|
+
category: 'reverse-shells',
|
|
702
|
+
description: 'uses tunneling service for external access',
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
regex: r(String.raw `\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{2,5}`),
|
|
706
|
+
patternId: 'hardcoded_ip_port',
|
|
707
|
+
severity: 'medium',
|
|
708
|
+
category: 'reverse-shells',
|
|
709
|
+
description: 'hardcoded IP address with port',
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
regex: r(String.raw `0\.0\.0\.0:\d+|INADDR_ANY`),
|
|
713
|
+
patternId: 'bind_all_interfaces',
|
|
714
|
+
severity: 'high',
|
|
715
|
+
category: 'reverse-shells',
|
|
716
|
+
description: 'binds to all network interfaces',
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
regex: r(String.raw `/bin/(ba)?sh\s+-i\s+.*>/dev/tcp/`),
|
|
720
|
+
patternId: 'bash_reverse_shell',
|
|
721
|
+
severity: 'critical',
|
|
722
|
+
category: 'reverse-shells',
|
|
723
|
+
description: 'bash reverse shell via /dev/tcp',
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
regex: r(String.raw `python[23]?\s+-c\s+["']import\s+socket`),
|
|
727
|
+
patternId: 'python_socket_oneliner',
|
|
728
|
+
severity: 'critical',
|
|
729
|
+
category: 'reverse-shells',
|
|
730
|
+
description: 'Python one-liner socket connection (likely reverse shell)',
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
regex: r(String.raw `socket\.connect\s*\(\s*\(`),
|
|
734
|
+
patternId: 'python_socket_connect',
|
|
735
|
+
severity: 'high',
|
|
736
|
+
category: 'reverse-shells',
|
|
737
|
+
description: 'Python socket connect to arbitrary host',
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
regex: r(String.raw `webhook\.site|requestbin\.com|pipedream\.net|hookbin\.com`),
|
|
741
|
+
patternId: 'exfil_service',
|
|
742
|
+
severity: 'high',
|
|
743
|
+
category: 'reverse-shells',
|
|
744
|
+
description: 'references known webhook/exfiltration service',
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
regex: r(String.raw `pastebin\.com|hastebin\.com|ghostbin\.`),
|
|
748
|
+
patternId: 'paste_service',
|
|
749
|
+
severity: 'medium',
|
|
750
|
+
category: 'reverse-shells',
|
|
751
|
+
description: 'references paste service (possible staging)',
|
|
752
|
+
},
|
|
177
753
|
// obfuscation
|
|
178
|
-
{
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
{
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
754
|
+
{
|
|
755
|
+
regex: r(String.raw `base64\s+(-d|--decode)\s*\|`),
|
|
756
|
+
patternId: 'base64_decode_pipe',
|
|
757
|
+
severity: 'high',
|
|
758
|
+
category: 'obfuscation',
|
|
759
|
+
description: 'base64 decode piped to execution',
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
regex: r(String.raw `\\x[0-9a-fA-F]{2}.*\\x[0-9a-fA-F]{2}.*\\x[0-9a-fA-F]{2}`),
|
|
763
|
+
patternId: 'hex_encoded_string',
|
|
764
|
+
severity: 'medium',
|
|
765
|
+
category: 'obfuscation',
|
|
766
|
+
description: 'hex-encoded string chain',
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
regex: r(String.raw `\beval\s*\(\s*["']`),
|
|
770
|
+
patternId: 'eval_string',
|
|
771
|
+
severity: 'high',
|
|
772
|
+
category: 'obfuscation',
|
|
773
|
+
description: 'eval() with string argument',
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
regex: r(String.raw `\bexec\s*\(\s*["']`),
|
|
777
|
+
patternId: 'exec_string',
|
|
778
|
+
severity: 'high',
|
|
779
|
+
category: 'obfuscation',
|
|
780
|
+
description: 'exec() with string argument',
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
regex: r(String.raw `echo\s+[^\n]*\|\s*(bash|sh|python|perl|ruby|node)`),
|
|
784
|
+
patternId: 'echo_pipe_exec',
|
|
785
|
+
severity: 'critical',
|
|
786
|
+
category: 'obfuscation',
|
|
787
|
+
description: 'echo piped to interpreter for execution',
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
regex: r(String.raw `compile\s*\(\s*[^\)]+,\s*["'].*["']\s*,\s*["']exec["']\s*\)`),
|
|
791
|
+
patternId: 'python_compile_exec',
|
|
792
|
+
severity: 'high',
|
|
793
|
+
category: 'obfuscation',
|
|
794
|
+
description: 'Python compile() with exec mode',
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
regex: r(String.raw `getattr\s*\(\s*__builtins__`),
|
|
798
|
+
patternId: 'python_getattr_builtins',
|
|
799
|
+
severity: 'high',
|
|
800
|
+
category: 'obfuscation',
|
|
801
|
+
description: 'dynamic access to Python builtins',
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
regex: r(String.raw `__import__\s*\(\s*["']os["']\s*\)`),
|
|
805
|
+
patternId: 'python_import_os',
|
|
806
|
+
severity: 'high',
|
|
807
|
+
category: 'obfuscation',
|
|
808
|
+
description: 'dynamic import of os module',
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
regex: r(String.raw `codecs\.decode\s*\(\s*["']`),
|
|
812
|
+
patternId: 'python_codecs_decode',
|
|
813
|
+
severity: 'medium',
|
|
814
|
+
category: 'obfuscation',
|
|
815
|
+
description: 'codecs.decode (possible obfuscation)',
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
regex: r(String.raw `String\.fromCharCode|charCodeAt`),
|
|
819
|
+
patternId: 'js_char_code',
|
|
820
|
+
severity: 'medium',
|
|
821
|
+
category: 'obfuscation',
|
|
822
|
+
description: 'JavaScript character code construction',
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
regex: r(String.raw `atob\s*\(|btoa\s*\(`),
|
|
826
|
+
patternId: 'js_base64',
|
|
827
|
+
severity: 'medium',
|
|
828
|
+
category: 'obfuscation',
|
|
829
|
+
description: 'JavaScript base64 encode/decode',
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
regex: r(String.raw `\[::-1\]`),
|
|
833
|
+
patternId: 'string_reversal',
|
|
834
|
+
severity: 'low',
|
|
835
|
+
category: 'obfuscation',
|
|
836
|
+
description: 'string reversal (possible obfuscation)',
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
regex: r(String.raw `chr\s*\(\s*\d+\s*\)\s*\+\s*chr\s*\(\s*\d+`),
|
|
840
|
+
patternId: 'chr_building',
|
|
841
|
+
severity: 'high',
|
|
842
|
+
category: 'obfuscation',
|
|
843
|
+
description: 'building string from chr() calls',
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
regex: r(String.raw `\\u[0-9a-fA-F]{4}.*\\u[0-9a-fA-F]{4}.*\\u[0-9a-fA-F]{4}`),
|
|
847
|
+
patternId: 'unicode_escape_chain',
|
|
848
|
+
severity: 'medium',
|
|
849
|
+
category: 'obfuscation',
|
|
850
|
+
description: 'chain of unicode escapes',
|
|
851
|
+
},
|
|
192
852
|
// supply-chain
|
|
193
|
-
{
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
{
|
|
201
|
-
|
|
202
|
-
|
|
853
|
+
{
|
|
854
|
+
regex: r(String.raw `curl\s+[^\n]*\|\s*(ba)?sh`),
|
|
855
|
+
patternId: 'curl_pipe_shell',
|
|
856
|
+
severity: 'critical',
|
|
857
|
+
category: 'supply-chain',
|
|
858
|
+
description: 'curl piped to shell (download-and-execute)',
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
regex: r(String.raw `wget\s+[^\n]*-O\s*-\s*\|\s*(ba)?sh`),
|
|
862
|
+
patternId: 'wget_pipe_shell',
|
|
863
|
+
severity: 'critical',
|
|
864
|
+
category: 'supply-chain',
|
|
865
|
+
description: 'wget piped to shell (download-and-execute)',
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
regex: r(String.raw `curl\s+[^\n]*\|\s*python`),
|
|
869
|
+
patternId: 'curl_pipe_python',
|
|
870
|
+
severity: 'critical',
|
|
871
|
+
category: 'supply-chain',
|
|
872
|
+
description: 'curl piped to Python interpreter',
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
regex: r(String.raw `#\s*///\s*script.*dependencies`),
|
|
876
|
+
patternId: 'pep723_inline_deps',
|
|
877
|
+
severity: 'medium',
|
|
878
|
+
category: 'supply-chain',
|
|
879
|
+
description: 'PEP 723 inline script dependencies (verify pinning)',
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
regex: r(String.raw `pip\s+install\s+(?!-r\s)(?!.*==)`),
|
|
883
|
+
patternId: 'unpinned_pip_install',
|
|
884
|
+
severity: 'medium',
|
|
885
|
+
category: 'supply-chain',
|
|
886
|
+
description: 'pip install without version pinning',
|
|
887
|
+
},
|
|
888
|
+
{
|
|
889
|
+
regex: r(String.raw `npm\s+install\s+(?!.*@\d)`),
|
|
890
|
+
patternId: 'unpinned_npm_install',
|
|
891
|
+
severity: 'medium',
|
|
892
|
+
category: 'supply-chain',
|
|
893
|
+
description: 'npm install without version pinning',
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
regex: r(String.raw `uv\s+run\s+`),
|
|
897
|
+
patternId: 'uv_run',
|
|
898
|
+
severity: 'medium',
|
|
899
|
+
category: 'supply-chain',
|
|
900
|
+
description: 'uv run may auto-install unpinned dependencies',
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
regex: r(String.raw `(curl|wget|httpx?\.get|requests\.get|fetch)\s*[\(]?\s*["']https?://`),
|
|
904
|
+
patternId: 'remote_fetch',
|
|
905
|
+
severity: 'medium',
|
|
906
|
+
category: 'supply-chain',
|
|
907
|
+
description: 'fetches remote resource at runtime',
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
regex: r(String.raw `git\s+clone\s+`),
|
|
911
|
+
patternId: 'git_clone',
|
|
912
|
+
severity: 'medium',
|
|
913
|
+
category: 'supply-chain',
|
|
914
|
+
description: 'clones git repository at runtime',
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
regex: r(String.raw `docker\s+pull\s+`),
|
|
918
|
+
patternId: 'docker_pull',
|
|
919
|
+
severity: 'medium',
|
|
920
|
+
category: 'supply-chain',
|
|
921
|
+
description: 'pulls Docker image at runtime',
|
|
922
|
+
},
|
|
203
923
|
// credential-exposure
|
|
204
|
-
{
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
924
|
+
{
|
|
925
|
+
regex: r(String.raw `(?:api[_-]?key|token|secret|password)\s*[=:]\s*["'][A-Za-z0-9+/=_-]{20,}`),
|
|
926
|
+
patternId: 'hardcoded_secret',
|
|
927
|
+
severity: 'critical',
|
|
928
|
+
category: 'credential-exposure',
|
|
929
|
+
description: 'possible hardcoded API key/token/secret',
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
regex: r(String.raw `-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----`),
|
|
933
|
+
patternId: 'embedded_private_key',
|
|
934
|
+
severity: 'critical',
|
|
935
|
+
category: 'credential-exposure',
|
|
936
|
+
description: 'embedded private key',
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
regex: r(String.raw `ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{80,}`),
|
|
940
|
+
patternId: 'github_token_leaked',
|
|
941
|
+
severity: 'critical',
|
|
942
|
+
category: 'credential-exposure',
|
|
943
|
+
description: 'GitHub personal access token in skill content',
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
regex: r(String.raw `sk-[A-Za-z0-9]{20,}`),
|
|
947
|
+
patternId: 'openai_key_leaked',
|
|
948
|
+
severity: 'critical',
|
|
949
|
+
category: 'credential-exposure',
|
|
950
|
+
description: 'possible OpenAI API key in skill content',
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
regex: r(String.raw `sk-ant-[A-Za-z0-9_-]{90,}`),
|
|
954
|
+
patternId: 'anthropic_key_leaked',
|
|
955
|
+
severity: 'critical',
|
|
956
|
+
category: 'credential-exposure',
|
|
957
|
+
description: 'possible Anthropic API key in skill content',
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
regex: r(String.raw `AKIA[0-9A-Z]{16}`),
|
|
961
|
+
patternId: 'aws_access_key_leaked',
|
|
962
|
+
severity: 'critical',
|
|
963
|
+
category: 'credential-exposure',
|
|
964
|
+
description: 'AWS access key ID in skill content',
|
|
965
|
+
},
|
|
210
966
|
];
|
|
211
967
|
function pathWithin(root, target) {
|
|
212
968
|
const rel = path.relative(root, target);
|
|
@@ -357,7 +1113,9 @@ function collectStructure(skillPath) {
|
|
|
357
1113
|
category: 'structural',
|
|
358
1114
|
file: relativePath,
|
|
359
1115
|
line: 0,
|
|
360
|
-
match: isBinary
|
|
1116
|
+
match: isBinary
|
|
1117
|
+
? `binary content${ext ? ` (${ext})` : ''}`
|
|
1118
|
+
: `binary extension: ${ext}`,
|
|
361
1119
|
description: 'binary/executable content should not be in a skill',
|
|
362
1120
|
}));
|
|
363
1121
|
}
|
|
@@ -401,7 +1159,9 @@ function collectStructure(skillPath) {
|
|
|
401
1159
|
function scanFile(entry) {
|
|
402
1160
|
if (entry.isBinary)
|
|
403
1161
|
return [];
|
|
404
|
-
if (entry.extension !== '.md' &&
|
|
1162
|
+
if (entry.extension !== '.md' &&
|
|
1163
|
+
entry.relativePath !== 'SKILL.md' &&
|
|
1164
|
+
!SCANNABLE_EXTENSIONS.has(entry.extension)) {
|
|
405
1165
|
return [];
|
|
406
1166
|
}
|
|
407
1167
|
let content;
|
|
@@ -442,7 +1202,8 @@ function scanFile(entry) {
|
|
|
442
1202
|
for (const char of INVISIBLE_CHARS) {
|
|
443
1203
|
if (!line.includes(char))
|
|
444
1204
|
continue;
|
|
445
|
-
const charName = INVISIBLE_CHAR_NAMES[char] ||
|
|
1205
|
+
const charName = INVISIBLE_CHAR_NAMES[char] ||
|
|
1206
|
+
`U+${char.codePointAt(0)?.toString(16).toUpperCase()}`;
|
|
446
1207
|
findings.push(createFinding({
|
|
447
1208
|
patternId: 'invisible_unicode',
|
|
448
1209
|
severity: 'high',
|
|
@@ -479,7 +1240,9 @@ function computeMtimeSignature(parts) {
|
|
|
479
1240
|
}
|
|
480
1241
|
function computeContentHash(files) {
|
|
481
1242
|
const hash = createHash('sha256');
|
|
482
|
-
const sortedFiles = files
|
|
1243
|
+
const sortedFiles = files
|
|
1244
|
+
.slice()
|
|
1245
|
+
.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
483
1246
|
for (const file of sortedFiles) {
|
|
484
1247
|
hash.update(file.relativePath).update('\0');
|
|
485
1248
|
hash.update(String(file.size)).update('\0');
|