agent-recon 1.0.1
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/hooks/send-event-wsl.py +339 -0
- package/.claude/hooks/send-event.py +334 -0
- package/CHANGELOG.md +66 -0
- package/CONTRIBUTING.md +70 -0
- package/EULA.md +223 -0
- package/INSTALL.md +193 -0
- package/LICENSE +287 -0
- package/LICENSE-COMMERCIAL +241 -0
- package/PRIVACY.md +115 -0
- package/README.md +182 -0
- package/SECURITY.md +63 -0
- package/TERMS.md +233 -0
- package/install-service.ps1 +302 -0
- package/installer/cli.js +177 -0
- package/installer/detect.js +355 -0
- package/installer/install.js +195 -0
- package/installer/manifest.js +140 -0
- package/installer/package.json +12 -0
- package/installer/steps/api-keys.js +59 -0
- package/installer/steps/directory.js +41 -0
- package/installer/steps/env-report.js +48 -0
- package/installer/steps/hooks.js +149 -0
- package/installer/steps/service.js +159 -0
- package/installer/steps/tls.js +104 -0
- package/installer/steps/verify.js +117 -0
- package/installer/steps/welcome.js +46 -0
- package/installer/ui.js +133 -0
- package/installer/uninstall.js +233 -0
- package/installer/upgrade.js +289 -0
- package/package.json +58 -0
- package/public/index.html +13953 -0
- package/server/fixtures/allowlist-profiles.json +185 -0
- package/server/package.json +34 -0
- package/server/platform.js +270 -0
- package/server/rules/gitleaks.toml +3214 -0
- package/server/rules/security.yara +579 -0
- package/server/start.js +178 -0
- package/service/agent-recon.service +30 -0
- package/service/com.agent-recon.server.plist +56 -0
- package/setup-linux.sh +259 -0
- package/setup-macos.sh +264 -0
- package/setup-wsl.sh +248 -0
- package/setup.ps1 +171 -0
- package/start-agent-recon.bat +4 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* security.yara — Agent Recon server-side security classification rules.
|
|
3
|
+
*
|
|
4
|
+
* These rules mirror the _SR regex patterns from security-llm.js so that
|
|
5
|
+
* server-side events receive the same classification as browser-side events.
|
|
6
|
+
*
|
|
7
|
+
* Meta fields carried by every rule:
|
|
8
|
+
* sec_cat — maps to secCat in the classification result
|
|
9
|
+
* sec_subcat — maps to secSubcat
|
|
10
|
+
* risk_level — "critical" | "high" | "medium" | "low"
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT: boolean meta values come back from @litko/yara-x as the string
|
|
13
|
+
* "unknown". All meta values here are strings.
|
|
14
|
+
*
|
|
15
|
+
* Priority order within each group (enforced by yara-scanner.js):
|
|
16
|
+
*
|
|
17
|
+
* Bash: bash_inject_code > bash_inject_pipe > bash_secret_any >
|
|
18
|
+
* bash_data_exfil > bash_exec_priv > bash_auth_tooling >
|
|
19
|
+
* bash_net_ssh_conn > bash_net_call > bash_file_sensitivepath
|
|
20
|
+
*
|
|
21
|
+
* Write: write_file_sensitivepath > write_secret_credwrite
|
|
22
|
+
* Read: read_file_sensitivepath
|
|
23
|
+
* Prompt: prompt_secret_promptsecret > prompt_inject_promptinject
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// ── Bash / command-string rules ───────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
rule bash_inject_code
|
|
29
|
+
{
|
|
30
|
+
meta:
|
|
31
|
+
sec_cat = "inject"
|
|
32
|
+
sec_subcat = "code-injection"
|
|
33
|
+
risk_level = "critical"
|
|
34
|
+
description = "Interpreter invoked with inline code string (python -c, node -e, etc.)"
|
|
35
|
+
|
|
36
|
+
strings:
|
|
37
|
+
// python3? -c followed by a quote
|
|
38
|
+
$py_c = /python3?\s+-c\s+["'`]/ nocase ascii
|
|
39
|
+
// node -e followed by a quote
|
|
40
|
+
$node_e = /node\s+-e\s+["'`]/ nocase ascii
|
|
41
|
+
// perl -e followed by a quote
|
|
42
|
+
$perl_e = /perl\s+-e\s+["'`]/ nocase ascii
|
|
43
|
+
// ruby -e followed by a quote
|
|
44
|
+
$ruby_e = /ruby\s+-e\s+["'`]/ nocase ascii
|
|
45
|
+
// exec( followed by a quote
|
|
46
|
+
$exec_q = /\bexec\s*\(\s*["']/ nocase ascii
|
|
47
|
+
|
|
48
|
+
condition:
|
|
49
|
+
any of them
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
rule bash_inject_pipe
|
|
53
|
+
{
|
|
54
|
+
meta:
|
|
55
|
+
sec_cat = "inject"
|
|
56
|
+
sec_subcat = "pipe-injection"
|
|
57
|
+
risk_level = "high"
|
|
58
|
+
description = "Pipe to shell or command substitution subshell"
|
|
59
|
+
|
|
60
|
+
strings:
|
|
61
|
+
// | sh or | bash
|
|
62
|
+
$pipe_sh = /\|\s*(?:ba)?sh\b/ nocase ascii
|
|
63
|
+
// $( ... ) with at least 5 chars inside
|
|
64
|
+
$cmd_sub = /\$\([^)]{5,}\)/ ascii
|
|
65
|
+
|
|
66
|
+
condition:
|
|
67
|
+
any of them
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
rule bash_secret_any
|
|
71
|
+
{
|
|
72
|
+
meta:
|
|
73
|
+
sec_cat = "secret"
|
|
74
|
+
sec_subcat = "credential-ref"
|
|
75
|
+
risk_level = "high"
|
|
76
|
+
description = "Reference to a secret or credential variable/assignment"
|
|
77
|
+
|
|
78
|
+
strings:
|
|
79
|
+
// ${API_KEY} or $API_KEY variants (case-insensitive variable names)
|
|
80
|
+
$var_api_key = /\$\{?API_KEY\}?/ nocase ascii
|
|
81
|
+
$var_secret = /\$\{?SECRET\}?/ nocase ascii
|
|
82
|
+
$var_token = /\$\{?TOKEN\}?/ nocase ascii
|
|
83
|
+
$var_password = /\$\{?PASSWORD\}?/ nocase ascii
|
|
84
|
+
$var_passwd = /\$\{?PASSWD\}?/ nocase ascii
|
|
85
|
+
$var_access_key = /\$\{?ACCESS_KEY\}?/ nocase ascii
|
|
86
|
+
$var_private_key = /\$\{?PRIVATE_KEY\}?/ nocase ascii
|
|
87
|
+
$var_client_secret = /\$\{?CLIENT_SECRET\}?/ nocase ascii
|
|
88
|
+
$var_auth_token = /\$\{?AUTH_TOKEN\}?/ nocase ascii
|
|
89
|
+
$var_github_token = /\$\{?GITHUB_TOKEN\}?/ nocase ascii
|
|
90
|
+
$var_openai_key = /\$\{?OPENAI_API_KEY\}?/ nocase ascii
|
|
91
|
+
$var_anthropic_key = /\$\{?ANTHROPIC_API_KEY\}?/ nocase ascii
|
|
92
|
+
// bare assignment forms: API_KEY=, SECRET=, TOKEN=
|
|
93
|
+
$assign_api_key = /\bAPI_KEY\s*=/ ascii
|
|
94
|
+
$assign_secret = /\bSECRET\s*=/ ascii
|
|
95
|
+
$assign_token = /\bTOKEN\s*=/ ascii
|
|
96
|
+
|
|
97
|
+
condition:
|
|
98
|
+
any of them
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
rule bash_data_exfil
|
|
102
|
+
{
|
|
103
|
+
meta:
|
|
104
|
+
sec_cat = "data"
|
|
105
|
+
sec_subcat = "exfiltration"
|
|
106
|
+
risk_level = "high"
|
|
107
|
+
description = "Data exfiltration pattern (dd, base64 encode/decode, archive of home, curl/wget POST)"
|
|
108
|
+
|
|
109
|
+
strings:
|
|
110
|
+
$dd_if = /\bdd\s+if=/ nocase ascii
|
|
111
|
+
$base64_de = /\bbase64\s+-[de]\b/ nocase ascii
|
|
112
|
+
$tar_home = /\btar\s+[cC].*\/home\b/ nocase ascii
|
|
113
|
+
$zip_home = /\bzip\s.*\/home\b/ nocase ascii
|
|
114
|
+
$curl_post = /\bcurl\s+[^|]*\s+-d\s/ nocase ascii
|
|
115
|
+
$wget_post = /\bwget\s+.*--post/ nocase ascii
|
|
116
|
+
|
|
117
|
+
condition:
|
|
118
|
+
any of them
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
rule bash_exec_priv
|
|
122
|
+
{
|
|
123
|
+
meta:
|
|
124
|
+
sec_cat = "exec"
|
|
125
|
+
sec_subcat = "priv-escalation"
|
|
126
|
+
risk_level = "high"
|
|
127
|
+
description = "Privilege escalation command (sudo, chmod permissive, chown root, setuid/setcap)"
|
|
128
|
+
|
|
129
|
+
strings:
|
|
130
|
+
$sudo = /\bsudo\b/ nocase ascii
|
|
131
|
+
$su_dash = /\bsu\s+-/ nocase ascii
|
|
132
|
+
// chmod with any digit combination containing 2,3,6,7 in owner/group/other
|
|
133
|
+
$chmod_perm = /\bchmod\s+[0-7]*[2367][0-9]{0,2}\b/ ascii
|
|
134
|
+
$chown_root = /\bchown\s+root/ nocase ascii
|
|
135
|
+
$setuid = /\bsetuid\b/ nocase ascii
|
|
136
|
+
$setcap = /\bsetcap\b/ nocase ascii
|
|
137
|
+
$visudo = /\bvisudo\b/ nocase ascii
|
|
138
|
+
$pkexec = /\bpkexec\b/ nocase ascii
|
|
139
|
+
$doas = /\bdoas\b/ nocase ascii
|
|
140
|
+
$newgrp = /\bnewgrp\b/ nocase ascii
|
|
141
|
+
|
|
142
|
+
condition:
|
|
143
|
+
any of them
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
rule bash_auth_tooling
|
|
147
|
+
{
|
|
148
|
+
meta:
|
|
149
|
+
sec_cat = "auth"
|
|
150
|
+
sec_subcat = "auth-tooling"
|
|
151
|
+
risk_level = "medium"
|
|
152
|
+
description = "Authentication or key management tooling (ssh-keygen, gpg, passwd, openssl genrsa, etc.)"
|
|
153
|
+
|
|
154
|
+
strings:
|
|
155
|
+
$ssh_keygen = /\bssh-keygen\b/ nocase ascii
|
|
156
|
+
$ssh_agent = /\bssh-agent\b/ nocase ascii
|
|
157
|
+
$gpg = /\bgpg\b/ nocase ascii
|
|
158
|
+
$gpg2 = /\bgpg2\b/ nocase ascii
|
|
159
|
+
// passwd omitted: without lookbehind it would match /etc/passwd paths, causing
|
|
160
|
+
// false positives. YARA-X does not support look-around assertions.
|
|
161
|
+
$useradd = /\buseradd\b/ nocase ascii
|
|
162
|
+
$usermod = /\busermod\b/ nocase ascii
|
|
163
|
+
$userdel = /\buserdel\b/ nocase ascii
|
|
164
|
+
$openssl_genrsa = /\bopenssl\s+genrsa/ nocase ascii
|
|
165
|
+
$openssl_req = /\bopenssl\s+req\b/ nocase ascii
|
|
166
|
+
$age_tool = /\bage\b/ nocase ascii
|
|
167
|
+
$keychain = /\bkeychain\b/ nocase ascii
|
|
168
|
+
|
|
169
|
+
condition:
|
|
170
|
+
any of them
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
rule bash_net_ssh_conn
|
|
174
|
+
{
|
|
175
|
+
meta:
|
|
176
|
+
sec_cat = "net"
|
|
177
|
+
sec_subcat = "ssh-connection"
|
|
178
|
+
risk_level = "medium"
|
|
179
|
+
description = "SSH connection to a remote host (user@host pattern)"
|
|
180
|
+
|
|
181
|
+
strings:
|
|
182
|
+
// ssh followed by optional flags then user@host
|
|
183
|
+
$ssh_conn = /\bssh\s+(-[A-Za-z]+\s+)*[a-zA-Z0-9_.-]+@/ ascii
|
|
184
|
+
|
|
185
|
+
condition:
|
|
186
|
+
any of them
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
rule bash_net_call
|
|
190
|
+
{
|
|
191
|
+
meta:
|
|
192
|
+
sec_cat = "net"
|
|
193
|
+
sec_subcat = "network-call"
|
|
194
|
+
risk_level = "low"
|
|
195
|
+
description = "Network utility invocation (curl, wget, nc, scp, etc.)"
|
|
196
|
+
|
|
197
|
+
strings:
|
|
198
|
+
// Note: ssh is intentionally omitted here — user@host connections are
|
|
199
|
+
// caught by bash_net_ssh_conn (higher priority) and key management by
|
|
200
|
+
// bash_auth_tooling. Lookahead exclusions unsupported in YARA-X.
|
|
201
|
+
$curl = /\bcurl\b/ nocase ascii
|
|
202
|
+
$wget = /\bwget\b/ nocase ascii
|
|
203
|
+
$scp = /\bscp\b/ nocase ascii
|
|
204
|
+
$sftp = /\bsftp\b/ nocase ascii
|
|
205
|
+
$nc = /\bnc\b/ nocase ascii
|
|
206
|
+
$ncat = /\bncat\b/ nocase ascii
|
|
207
|
+
$socat = /\bsocat\b/ nocase ascii
|
|
208
|
+
$telnet = /\btelnet\b/ nocase ascii
|
|
209
|
+
$nmap = /\bnmap\b/ nocase ascii
|
|
210
|
+
$rsync = /\brsync\b/ nocase ascii
|
|
211
|
+
$ftp = /\bftp\b/ nocase ascii
|
|
212
|
+
|
|
213
|
+
condition:
|
|
214
|
+
any of them
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
rule bash_file_sensitivepath
|
|
218
|
+
{
|
|
219
|
+
meta:
|
|
220
|
+
sec_cat = "file"
|
|
221
|
+
sec_subcat = "sensitive-path"
|
|
222
|
+
risk_level = "medium"
|
|
223
|
+
description = "Reference to a sensitive filesystem path (SSH keys, AWS creds, /etc/shadow, etc.)"
|
|
224
|
+
|
|
225
|
+
strings:
|
|
226
|
+
$ssh_dir = /\/\.ssh\b/ nocase ascii
|
|
227
|
+
$aws_dir = /\/\.aws\b/ nocase ascii
|
|
228
|
+
$gnupg_dir = /\/\.gnupg\b/ nocase ascii
|
|
229
|
+
$netrc = /\/\.netrc\b/ nocase ascii
|
|
230
|
+
$docker_dir = /\/\.docker\b/ nocase ascii
|
|
231
|
+
$etc_passwd = /\/etc\/passwd\b/ nocase ascii
|
|
232
|
+
$etc_shadow = /\/etc\/shadow\b/ nocase ascii
|
|
233
|
+
$etc_sudoers = /\/etc\/sudoers\b/ nocase ascii
|
|
234
|
+
$etc_cron = /\/etc\/cron\b/ nocase ascii
|
|
235
|
+
$etc_ssh = /\/etc\/ssh\b/ nocase ascii
|
|
236
|
+
$etc_hosts = /\/etc\/hosts\b/ nocase ascii
|
|
237
|
+
$credential = /credential/ nocase ascii
|
|
238
|
+
$dotkey = /\.key$/ nocase ascii
|
|
239
|
+
$dotpem = /\.pem$/ nocase ascii
|
|
240
|
+
$dotenv_ext = /\.env$/ nocase ascii
|
|
241
|
+
$dotenv_word = /\.env\b/ nocase ascii
|
|
242
|
+
$id_rsa = /id_rsa/ nocase ascii
|
|
243
|
+
$auth_keys = /authorized_keys/ nocase ascii
|
|
244
|
+
$dottoken = /\.token\b/ nocase ascii
|
|
245
|
+
|
|
246
|
+
condition:
|
|
247
|
+
any of them
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ── Write path rules ──────────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
rule write_file_sensitivepath
|
|
253
|
+
{
|
|
254
|
+
meta:
|
|
255
|
+
sec_cat = "file"
|
|
256
|
+
sec_subcat = "sensitive-write"
|
|
257
|
+
risk_level = "high"
|
|
258
|
+
description = "Write to a sensitive file path"
|
|
259
|
+
|
|
260
|
+
strings:
|
|
261
|
+
$ssh_dir = /\/\.ssh\b/ nocase ascii
|
|
262
|
+
$aws_dir = /\/\.aws\b/ nocase ascii
|
|
263
|
+
$gnupg_dir = /\/\.gnupg\b/ nocase ascii
|
|
264
|
+
$netrc = /\/\.netrc\b/ nocase ascii
|
|
265
|
+
$docker_dir = /\/\.docker\b/ nocase ascii
|
|
266
|
+
$etc_passwd = /\/etc\/passwd\b/ nocase ascii
|
|
267
|
+
$etc_shadow = /\/etc\/shadow\b/ nocase ascii
|
|
268
|
+
$etc_sudoers = /\/etc\/sudoers\b/ nocase ascii
|
|
269
|
+
$etc_cron = /\/etc\/cron\b/ nocase ascii
|
|
270
|
+
$etc_ssh = /\/etc\/ssh\b/ nocase ascii
|
|
271
|
+
$etc_hosts = /\/etc\/hosts\b/ nocase ascii
|
|
272
|
+
$credential = /credential/ nocase ascii
|
|
273
|
+
$dotkey = /\.key$/ nocase ascii
|
|
274
|
+
$dotpem = /\.pem$/ nocase ascii
|
|
275
|
+
$dotenv_ext = /\.env$/ nocase ascii
|
|
276
|
+
$dotenv_word = /\.env\b/ nocase ascii
|
|
277
|
+
$id_rsa = /id_rsa/ nocase ascii
|
|
278
|
+
$auth_keys = /authorized_keys/ nocase ascii
|
|
279
|
+
$dottoken = /\.token\b/ nocase ascii
|
|
280
|
+
|
|
281
|
+
condition:
|
|
282
|
+
any of them
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
rule write_secret_credwrite
|
|
286
|
+
{
|
|
287
|
+
meta:
|
|
288
|
+
sec_cat = "secret"
|
|
289
|
+
sec_subcat = "credential-write"
|
|
290
|
+
risk_level = "high"
|
|
291
|
+
description = "Content being written contains a credential or secret reference"
|
|
292
|
+
|
|
293
|
+
strings:
|
|
294
|
+
$var_api_key = /\$\{?API_KEY\}?/ nocase ascii
|
|
295
|
+
$var_secret = /\$\{?SECRET\}?/ nocase ascii
|
|
296
|
+
$var_token = /\$\{?TOKEN\}?/ nocase ascii
|
|
297
|
+
$var_password = /\$\{?PASSWORD\}?/ nocase ascii
|
|
298
|
+
$var_passwd = /\$\{?PASSWD\}?/ nocase ascii
|
|
299
|
+
$var_access_key = /\$\{?ACCESS_KEY\}?/ nocase ascii
|
|
300
|
+
$var_private_key = /\$\{?PRIVATE_KEY\}?/ nocase ascii
|
|
301
|
+
$var_client_secret = /\$\{?CLIENT_SECRET\}?/ nocase ascii
|
|
302
|
+
$var_auth_token = /\$\{?AUTH_TOKEN\}?/ nocase ascii
|
|
303
|
+
$var_github_token = /\$\{?GITHUB_TOKEN\}?/ nocase ascii
|
|
304
|
+
$var_openai_key = /\$\{?OPENAI_API_KEY\}?/ nocase ascii
|
|
305
|
+
$var_anthropic_key = /\$\{?ANTHROPIC_API_KEY\}?/ nocase ascii
|
|
306
|
+
$assign_api_key = /\bAPI_KEY\s*=/ ascii
|
|
307
|
+
$assign_secret = /\bSECRET\s*=/ ascii
|
|
308
|
+
$assign_token = /\bTOKEN\s*=/ ascii
|
|
309
|
+
|
|
310
|
+
condition:
|
|
311
|
+
any of them
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ── Read / Glob / Grep path rules ─────────────────────────────────────────────
|
|
315
|
+
|
|
316
|
+
rule read_file_sensitivepath
|
|
317
|
+
{
|
|
318
|
+
meta:
|
|
319
|
+
sec_cat = "file"
|
|
320
|
+
sec_subcat = "sensitive-read"
|
|
321
|
+
risk_level = "medium"
|
|
322
|
+
description = "Read/Glob/Grep targeting a sensitive file path"
|
|
323
|
+
|
|
324
|
+
strings:
|
|
325
|
+
$ssh_dir = /\/\.ssh\b/ nocase ascii
|
|
326
|
+
$aws_dir = /\/\.aws\b/ nocase ascii
|
|
327
|
+
$gnupg_dir = /\/\.gnupg\b/ nocase ascii
|
|
328
|
+
$netrc = /\/\.netrc\b/ nocase ascii
|
|
329
|
+
$docker_dir = /\/\.docker\b/ nocase ascii
|
|
330
|
+
$etc_passwd = /\/etc\/passwd\b/ nocase ascii
|
|
331
|
+
$etc_shadow = /\/etc\/shadow\b/ nocase ascii
|
|
332
|
+
$etc_sudoers = /\/etc\/sudoers\b/ nocase ascii
|
|
333
|
+
$etc_cron = /\/etc\/cron\b/ nocase ascii
|
|
334
|
+
$etc_ssh = /\/etc\/ssh\b/ nocase ascii
|
|
335
|
+
$etc_hosts = /\/etc\/hosts\b/ nocase ascii
|
|
336
|
+
$credential = /credential/ nocase ascii
|
|
337
|
+
$dotkey = /\.key$/ nocase ascii
|
|
338
|
+
$dotpem = /\.pem$/ nocase ascii
|
|
339
|
+
$dotenv_ext = /\.env$/ nocase ascii
|
|
340
|
+
$dotenv_word = /\.env\b/ nocase ascii
|
|
341
|
+
$id_rsa = /id_rsa/ nocase ascii
|
|
342
|
+
$auth_keys = /authorized_keys/ nocase ascii
|
|
343
|
+
$dottoken = /\.token\b/ nocase ascii
|
|
344
|
+
|
|
345
|
+
condition:
|
|
346
|
+
any of them
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ── UserPromptSubmit rules ────────────────────────────────────────────────────
|
|
350
|
+
|
|
351
|
+
rule prompt_secret_promptsecret
|
|
352
|
+
{
|
|
353
|
+
meta:
|
|
354
|
+
sec_cat = "secret"
|
|
355
|
+
sec_subcat = "prompt-secret"
|
|
356
|
+
risk_level = "medium"
|
|
357
|
+
description = "User prompt contains a credential or secret reference"
|
|
358
|
+
|
|
359
|
+
strings:
|
|
360
|
+
$var_api_key = /\$\{?API_KEY\}?/ nocase ascii
|
|
361
|
+
$var_secret = /\$\{?SECRET\}?/ nocase ascii
|
|
362
|
+
$var_token = /\$\{?TOKEN\}?/ nocase ascii
|
|
363
|
+
$var_password = /\$\{?PASSWORD\}?/ nocase ascii
|
|
364
|
+
$var_passwd = /\$\{?PASSWD\}?/ nocase ascii
|
|
365
|
+
$var_access_key = /\$\{?ACCESS_KEY\}?/ nocase ascii
|
|
366
|
+
$var_private_key = /\$\{?PRIVATE_KEY\}?/ nocase ascii
|
|
367
|
+
$var_client_secret = /\$\{?CLIENT_SECRET\}?/ nocase ascii
|
|
368
|
+
$var_auth_token = /\$\{?AUTH_TOKEN\}?/ nocase ascii
|
|
369
|
+
$var_github_token = /\$\{?GITHUB_TOKEN\}?/ nocase ascii
|
|
370
|
+
$var_openai_key = /\$\{?OPENAI_API_KEY\}?/ nocase ascii
|
|
371
|
+
$var_anthropic_key = /\$\{?ANTHROPIC_API_KEY\}?/ nocase ascii
|
|
372
|
+
$assign_api_key = /\bAPI_KEY\s*=/ ascii
|
|
373
|
+
$assign_secret = /\bSECRET\s*=/ ascii
|
|
374
|
+
$assign_token = /\bTOKEN\s*=/ ascii
|
|
375
|
+
|
|
376
|
+
condition:
|
|
377
|
+
any of them
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
rule prompt_inject_promptinject
|
|
381
|
+
{
|
|
382
|
+
meta:
|
|
383
|
+
sec_cat = "inject"
|
|
384
|
+
sec_subcat = "prompt-injection"
|
|
385
|
+
risk_level = "medium"
|
|
386
|
+
description = "User prompt contains a shell injection pattern"
|
|
387
|
+
|
|
388
|
+
strings:
|
|
389
|
+
// | sh or | bash
|
|
390
|
+
$pipe_sh = /\|\s*(?:ba)?sh\b/ nocase ascii
|
|
391
|
+
// $( ... ) with at least 5 chars inside
|
|
392
|
+
$cmd_sub = /\$\([^)]{5,}\)/ ascii
|
|
393
|
+
|
|
394
|
+
condition:
|
|
395
|
+
any of them
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// ── Gitleaks-derived credential patterns ──────────────────────────────────────
|
|
399
|
+
//
|
|
400
|
+
// High-confidence token/key format rules derived from the Gitleaks project
|
|
401
|
+
// (https://github.com/gitleaks/gitleaks — MIT licence). These match
|
|
402
|
+
// provider-specific key formats whose structure is publicly documented and
|
|
403
|
+
// effectively impossible to confuse with normal text.
|
|
404
|
+
|
|
405
|
+
// GitHub Personal Access Token (classic ghp_), GitHub App installation token
|
|
406
|
+
// (ghs_), and fine-grained PAT (github_pat_).
|
|
407
|
+
// Ref: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-authentication-to-github
|
|
408
|
+
rule gitleaks_github_pat
|
|
409
|
+
{
|
|
410
|
+
meta:
|
|
411
|
+
sec_cat = "secret"
|
|
412
|
+
sec_subcat = "github-token"
|
|
413
|
+
risk_level = "high"
|
|
414
|
+
description = "GitHub Personal Access Token (ghp_), App token (ghs_), or fine-grained PAT (github_pat_)"
|
|
415
|
+
|
|
416
|
+
strings:
|
|
417
|
+
// Classic PAT: ghp_ + 36 alphanumeric chars
|
|
418
|
+
$ghp = /ghp_[0-9a-zA-Z]{36}/ ascii
|
|
419
|
+
// GitHub App installation token: ghs_ + 36 alphanumeric chars
|
|
420
|
+
$ghs = /ghs_[0-9a-zA-Z]{36}/ ascii
|
|
421
|
+
// Fine-grained PAT: github_pat_ + 82 alphanumeric/underscore chars
|
|
422
|
+
$github_pat = /github_pat_[0-9a-zA-Z_]{82}/ ascii
|
|
423
|
+
|
|
424
|
+
condition:
|
|
425
|
+
any of them
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Anthropic API key — the sk-ant- prefix is unique to Anthropic.
|
|
429
|
+
// Real keys are 108 chars total; the pattern uses 20+ to be forward-compatible.
|
|
430
|
+
rule gitleaks_anthropic_key
|
|
431
|
+
{
|
|
432
|
+
meta:
|
|
433
|
+
sec_cat = "secret"
|
|
434
|
+
sec_subcat = "anthropic-api-key"
|
|
435
|
+
risk_level = "high"
|
|
436
|
+
description = "Anthropic API key (sk-ant- prefix followed by base64url payload)"
|
|
437
|
+
|
|
438
|
+
strings:
|
|
439
|
+
// sk-ant- + at least 20 alphanumeric/dash/underscore chars
|
|
440
|
+
$sk_ant = /sk-ant-[a-zA-Z0-9\-_]{20,}/ ascii
|
|
441
|
+
|
|
442
|
+
condition:
|
|
443
|
+
any of them
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// OpenAI API key formats.
|
|
447
|
+
// New project-scoped keys use sk-proj-; legacy service keys are sk- + 48 chars.
|
|
448
|
+
// Anthropic keys (sk-ant-) are excluded by the more-specific prefix patterns.
|
|
449
|
+
rule gitleaks_openai_key
|
|
450
|
+
{
|
|
451
|
+
meta:
|
|
452
|
+
sec_cat = "secret"
|
|
453
|
+
sec_subcat = "openai-api-key"
|
|
454
|
+
risk_level = "high"
|
|
455
|
+
description = "OpenAI API key — project-scoped (sk-proj-...) or legacy 48-char key"
|
|
456
|
+
|
|
457
|
+
strings:
|
|
458
|
+
// New project-scoped key: sk-proj- + 50+ alphanumeric/dash/underscore
|
|
459
|
+
$sk_proj = /sk-proj-[a-zA-Z0-9\-_]{50,}/ ascii
|
|
460
|
+
// Legacy service key: sk- + exactly 48 alphanumeric chars
|
|
461
|
+
// (must NOT be sk-ant- or sk-proj- — the specific length distinguishes it)
|
|
462
|
+
$sk_legacy = /sk-[a-zA-Z0-9]{48}/ ascii
|
|
463
|
+
|
|
464
|
+
condition:
|
|
465
|
+
any of them
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// AWS Access Key ID — fixed 20-char string beginning with one of the
|
|
469
|
+
// documented IAM key prefixes.
|
|
470
|
+
// Ref: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html
|
|
471
|
+
rule gitleaks_aws_access_key
|
|
472
|
+
{
|
|
473
|
+
meta:
|
|
474
|
+
sec_cat = "secret"
|
|
475
|
+
sec_subcat = "aws-access-key"
|
|
476
|
+
risk_level = "high"
|
|
477
|
+
description = "AWS Access Key ID (AKIA*, AGPA*, AIDA*, AROA*, AIPA*, ANPA*, ANVA*, ASIA*, A3T*)"
|
|
478
|
+
|
|
479
|
+
strings:
|
|
480
|
+
// 4-char prefix + 16 uppercase alphanumeric chars = 20-char key
|
|
481
|
+
$aws_key = /(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}/ ascii
|
|
482
|
+
|
|
483
|
+
condition:
|
|
484
|
+
any of them
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Generic hardcoded secret assignment — catches config files, .env files, and
|
|
488
|
+
// source code that embeds plaintext secrets as key = "value" pairs.
|
|
489
|
+
// Requires at least 8 chars in the value to reduce noise from test fixtures.
|
|
490
|
+
rule gitleaks_generic_secret
|
|
491
|
+
{
|
|
492
|
+
meta:
|
|
493
|
+
sec_cat = "secret"
|
|
494
|
+
sec_subcat = "hardcoded-secret"
|
|
495
|
+
risk_level = "high"
|
|
496
|
+
description = "Explicit secret assignment: password/secret/api_key/token = \"value\" with 8+ char value"
|
|
497
|
+
|
|
498
|
+
strings:
|
|
499
|
+
// (password|passwd|secret|api_key|apikey|token|auth_token) : "value" or = "value"
|
|
500
|
+
$assign = /(?i)(password|passwd|secret|api_key|apikey|token|auth_token)\s*[:=]\s*["'][^"']{8,}["']/ ascii
|
|
501
|
+
|
|
502
|
+
condition:
|
|
503
|
+
any of them
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// PEM/DER private key material — the canonical -----BEGIN ... PRIVATE KEY-----
|
|
507
|
+
// header is present in all common private key formats.
|
|
508
|
+
rule gitleaks_private_key
|
|
509
|
+
{
|
|
510
|
+
meta:
|
|
511
|
+
sec_cat = "secret"
|
|
512
|
+
sec_subcat = "private-key"
|
|
513
|
+
risk_level = "high"
|
|
514
|
+
description = "PEM private key header (RSA, EC, DSA, OpenSSH, or generic PRIVATE KEY block)"
|
|
515
|
+
|
|
516
|
+
strings:
|
|
517
|
+
// Matches -----BEGIN PRIVATE KEY-----, -----BEGIN RSA PRIVATE KEY-----, etc.
|
|
518
|
+
$pem = /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/ ascii
|
|
519
|
+
|
|
520
|
+
condition:
|
|
521
|
+
any of them
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// JSON Web Token — three base64url-encoded segments separated by dots.
|
|
525
|
+
// The eyJ prefix is deterministic: base64url of '{"' is always eyJ.
|
|
526
|
+
rule gitleaks_jwt
|
|
527
|
+
{
|
|
528
|
+
meta:
|
|
529
|
+
sec_cat = "secret"
|
|
530
|
+
sec_subcat = "jwt-token"
|
|
531
|
+
risk_level = "medium"
|
|
532
|
+
description = "JSON Web Token (JWT) — eyJ header . payload . signature, each segment 10+ chars"
|
|
533
|
+
|
|
534
|
+
strings:
|
|
535
|
+
// eyJ<header>.<payload>.<signature> — each segment at least 10 base64url chars
|
|
536
|
+
$jwt = /eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}/ ascii
|
|
537
|
+
|
|
538
|
+
condition:
|
|
539
|
+
any of them
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// ── Agentic compound rules ─────────────────────────────────────────────────────
|
|
543
|
+
//
|
|
544
|
+
// These rules fire on agent dispatch payloads (Task/Agent tool calls) whose
|
|
545
|
+
// description text explicitly references credential or secret material. A
|
|
546
|
+
// parent agent instructing a sub-agent to "fetch the API key" is materially
|
|
547
|
+
// more concerning than a sub-agent that incidentally touches a key store.
|
|
548
|
+
|
|
549
|
+
// Fires when a Task/Agent dispatch description combines an agent-spawn keyword
|
|
550
|
+
// with at least two distinct credential-domain terms. The two-keyword threshold
|
|
551
|
+
// avoids false positives from incidental single-word matches.
|
|
552
|
+
rule compound_agent_credaccess
|
|
553
|
+
{
|
|
554
|
+
meta:
|
|
555
|
+
sec_cat = "agent-spawn"
|
|
556
|
+
sec_subcat = "credentialed-subagent"
|
|
557
|
+
risk_level = "high"
|
|
558
|
+
description = "Agent dispatch (Task/Agent tool) whose description explicitly references credential or secret material — sub-agent authorised to handle secrets"
|
|
559
|
+
|
|
560
|
+
strings:
|
|
561
|
+
// Agent dispatch indicators
|
|
562
|
+
$dispatch_task = "Task(" ascii
|
|
563
|
+
$dispatch_agent = "Agent(" ascii
|
|
564
|
+
$dispatch_spawn = "spawn" nocase ascii
|
|
565
|
+
$dispatch_deleg = "delegate" nocase ascii
|
|
566
|
+
|
|
567
|
+
// Credential-domain keywords (each counted individually)
|
|
568
|
+
$cred_credentials = "credentials" nocase ascii
|
|
569
|
+
$cred_api_key_u = "api_key" nocase ascii
|
|
570
|
+
$cred_api_key_s = "api key" nocase ascii
|
|
571
|
+
$cred_secret = "secret" nocase ascii
|
|
572
|
+
$cred_password = "password" nocase ascii
|
|
573
|
+
$cred_private_key = "private key" nocase ascii
|
|
574
|
+
$cred_ssh_key = "ssh key" nocase ascii
|
|
575
|
+
|
|
576
|
+
condition:
|
|
577
|
+
// Must look like an agent dispatch AND reference at least 2 credential terms
|
|
578
|
+
(any of ($dispatch_*)) and (2 of ($cred_*))
|
|
579
|
+
}
|