-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathcoding_agents_rules.yaml
More file actions
2320 lines (2116 loc) · 105 KB
/
Copy pathcoding_agents_rules.yaml
File metadata and controls
2320 lines (2116 loc) · 105 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Default ruleset for Prempti.
# Shipped with the project. Overwritten on upgrade.
# Users should add custom rules in the user/ directory instead of editing this file.
# Users can extend the lists below using Falco's override mechanism in user/ rules.
#
# File layout (sections):
# 1. Working-directory boundary
# 2. Sensitive paths
# 3. Sandbox disable
# 4. Threats — credentials, dangerous commands, exfiltration, supply chain
# 5. MCP and skill content
# 6. Persistence vectors
#
# Output convention:
# The rule output is an LLM-friendly sentence that explains what happened and
# why the verdict was applied. It must start with "Falco" to attribute the
# verdict. Use resolved field values (%tool.real_file_path, %agent.real_cwd)
# to make the message informative. Keep it clean — no structured key=value pairs.
#
# correlation.id is a suggested output field (add_output in the plugin) and is
# always included in output_fields. The catch-all seen rule (seen.yaml) includes
# all fields for audit logging. Deny/ask rules only need the fields they reference.
#
# The append_output config appends an AI agent instruction to every alert.
# The broker constructs the verdict reason as: "Rule Name: <rendered message>".
#
# Example reason seen by the coding agent:
# Deny writing to sensitive paths: Falco blocked writing to /etc/passwd
# because it is a sensitive path | For AI Agents: inform the user that
# this action was flagged by a Falco rule | correlation=42
#
# Priority convention:
# CRITICAL — deny verdict
# WARNING — ask verdict
# NOTICE — monitor only (no verdict tag)
# DEBUG — reserved for the catch-all seen rule (seen.yaml)
# ---------------------------------------------------------------------------
# Lists
# ---------------------------------------------------------------------------
- list: sensitive_paths
items:
- /etc/
- /root/
- /var/
- /boot/
- /proc/
- /sys/
- list: sensitive_file_names
items:
- .env
- .env.local
- .env.production
- .netrc
- .npmrc
- credentials.json
- token.json
# Shell startup files that execute on every shell login.
- list: shell_startup_files
items: [.bashrc, .zshrc, .profile, .bash_profile, .zprofile, .bash_login, .bash_logout]
# Agent instruction files that other coding agents read as prompt context.
- list: agent_instruction_files
items: [.cursorrules, .windsurfrules, .aiderrules, AGENTS.md]
# Environment variable files commonly sourced by shells, frameworks, and CI.
- list: env_file_names
items:
- .env
- .envrc
- .env.local
- .env.development
- .env.production
- .env.staging
- .env.test
- .env.ci
- .env.override
# Package manager configuration files that control where packages are downloaded.
- list: registry_config_files
items: [.npmrc, .pypirc, pip.conf, .pnpmrc, .yarnrc.yml]
# ---------------------------------------------------------------------------
# Macros — shared
# ---------------------------------------------------------------------------
# File-mutation detection across agents. Claude Code calls Write/Edit at the
# hook layer; Codex routes all file mutations through apply_patch, with the
# specific operation surfaced as tool.patch_op (one of Add/Update/Delete/Move).
# All four are destructive in the sense the default ruleset cares about: a
# rule denying writes to /etc/* should also deny `*** Delete File: /etc/*`,
# and an outside-cwd ask should fire equally for create, modify, delete, or
# rename. The macro intentionally treats them uniformly. Rules that need
# operation-level granularity can still pattern-match on tool.patch_op
# directly (e.g. `tool.patch_op = "Delete" and tool.real_file_path …`).
- macro: is_claude_write_tool
condition: tool.name in ("Write", "Edit")
- macro: is_codex_patch_write
condition: tool.name = "apply_patch" and tool.patch_op in ("Add", "Update", "Delete", "Move")
- macro: is_write_tool
condition: is_claude_write_tool or is_codex_patch_write
# Extensible macro: each line is an "or" condition.
# Users can append custom sensitive paths via override:
# - macro: is_sensitive_path
# condition: or tool.real_file_path contains "/.my_secret_dir/"
# override:
# condition: append
- macro: is_sensitive_path
condition: >
tool.real_file_path pmatch (sensitive_paths)
or tool.real_file_path contains "/.ssh/"
or tool.real_file_path contains "/.gnupg/"
or tool.real_file_path contains "/.aws/"
or tool.real_file_path contains "/.azure/"
or tool.real_file_path contains "/.config/gcloud/"
or tool.real_file_path contains "/.kube/"
or tool.real_file_path contains "/.docker/"
or tool.file_name in (sensitive_file_names)
or basename(tool.real_file_path) in (sensitive_file_names)
- macro: is_outside_cwd
condition: >
agent.real_cwd = ""
or (tool.real_file_path != val(agent.real_cwd)
and not tool.real_file_path startswith val(agent.real_cwd_prefix))
# Claude Code's own data tree (~/.claude/). The agent legitimately writes
# session bookkeeping here — plans, todos, memories, transcripts, caches,
# shell snapshots, file history, telemetry — and these writes are routine,
# not security-sensitive. This macro is intentionally broad: it suppresses
# the generic "outside working directory" ASK across the whole tree so the
# agent can manage its own state without prompting.
#
# Sensitive subpaths are guarded by independent rules that fire regardless
# of this suppression (verdict escalation: deny > ask > allow):
# DENY — settings.json, settings.local.json (hooks), policy-limits.json
# ASK — commands/, CLAUDE.md, agents/, skills/, plugins/, backups/
- macro: is_claude_data_path
condition: tool.real_file_path contains "/.claude/"
# IOC hosting domains observed in the ClawHavoc campaign (341 malicious skills,
# 2024-2025) and similar AI-supply-chain attacks. Inlined into the macros below
# rather than a Falco list because Falco's condition language has no
# substring-match-against-list operator (only "in"/"pmatch", which require
# exact or prefix match — not what we need against a JSON blob or a Bash command).
# Extend by overriding the macros (condition: append) in user/ rules.
#
# Matches an IOC domain anywhere in the tool input JSON (Write/Edit content).
- macro: contains_ioc_domain
condition: >
tool.input contains "pastebin.com"
or tool.input contains "transfer.sh"
or tool.input contains "file.io"
or tool.input contains "termbin.com"
or tool.input contains "ix.io"
or tool.input contains "glot.io"
or tool.input contains "hastebin.com"
or tool.input contains "ghostbin.co"
# Matches an IOC domain in a Bash command string.
- macro: cmd_contains_ioc_domain
condition: >
tool.input_command contains "pastebin.com"
or tool.input_command contains "transfer.sh"
or tool.input_command contains "file.io"
or tool.input_command contains "termbin.com"
or tool.input_command contains "ix.io"
or tool.input_command contains "glot.io"
or tool.input_command contains "hastebin.com"
or tool.input_command contains "ghostbin.co"
# ===========================================================================
# Section 1 — Working-directory boundary
# ===========================================================================
- rule: Monitor activity outside working directory
desc: >
Logs when a coding agent reads, writes, or edits files outside the
session's working directory. Informational only — does not block.
condition: >
tool.name in ("Write", "Edit", "Read")
and tool.real_file_path != ""
and is_outside_cwd
output: >
Falco detected access to %tool.real_file_path which is outside the working directory %agent.real_cwd
priority: NOTICE
source: coding_agent
tags: []
- rule: Ask before writing outside working directory
desc: >
Requires user confirmation when a coding agent writes or edits a file
outside the session's working directory. Claude Code's own data tree
(~/.claude/) is excluded as routine session bookkeeping — plans,
todos, memories, transcripts, caches. Sensitive subpaths under
~/.claude/ (settings.json, policy-limits.json, agents/, skills/,
plugins/, backups/, commands/, CLAUDE.md) are still guarded by
dedicated deny/ask rules.
condition: >
is_write_tool
and tool.real_file_path != ""
and is_outside_cwd
and not is_sensitive_path
and not is_claude_data_path
output: >
Falco requires confirmation to write to %tool.real_file_path because it is outside the working directory %agent.real_cwd
priority: WARNING
source: coding_agent
tags: [coding_agent_ask]
# ===========================================================================
# Section 2 — Sensitive paths
# ===========================================================================
- rule: Deny reading sensitive paths
desc: >
Blocks reads from sensitive filesystem locations such as ~/.ssh/,
cloud credential directories, and environment variable files.
Prevents exfiltration of secrets via the Read tool.
Extend the sensitive_paths and sensitive_file_names lists in user/ rules
to customize.
condition: >
tool.name = "Read"
and is_sensitive_path
output: >
Falco blocked reading %tool.real_file_path because it is a sensitive path
priority: CRITICAL
source: coding_agent
tags: [coding_agent_deny, mitre_t1552_unsecured_credentials]
- rule: Deny writing to sensitive paths
desc: >
Blocks writes to sensitive filesystem locations such as /etc/, ~/.ssh/,
cloud credential directories, and environment variable files.
Extend the sensitive_paths and sensitive_file_names lists in user/ rules
to customize.
condition: >
is_write_tool
and is_sensitive_path
output: >
Falco blocked writing to %tool.real_file_path because it is a sensitive path
priority: CRITICAL
source: coding_agent
tags: [coding_agent_deny]
# ===========================================================================
# Section 3 — Sandbox disable
# ===========================================================================
# Detects prompt injection attacks directing agents to disable their own
# OS-level sandbox isolation, and per-command sandbox escape requests.
#
# Coverage:
# Rule A (ASK): Agent writing sandbox-disable content to its own config file
# — Claude Code (sandbox.enabled: false/0/null in settings.json or
# settings.local.json), Codex (danger-full-access in config.toml),
# Gemini CLI (tools.sandbox: false/none/disabled in settings.json)
# ASK rather than DENY: the content macros (e.g., "enabled" + "false")
# match common JSON shapes that surface in unrelated settings.json
# edits via the Edit tool's old_string/new_string capture.
# Rule B (ASK): Claude Code per-command escape via dangerouslyDisableSandbox:true
# Bash tool parameter — the ONLY layer that can detect this
# since it is a tool call parameter invisible to Falco syscalls.
# Rule C (DENY): Bash command writing sandbox-disable content to an agent
# settings file — catches echo/python/tee/cat redirection that
# bypasses the Write/Edit tool check entirely.
# Rule D (DENY): Codex CLI startup sandbox bypass flags via Bash
# — --dangerously-bypass-approvals-and-sandbox (dash and underscore),
# --sandbox danger-full-access
# Rule E (DENY): GEMINI_SANDBOX environment variable set to a disabling value
# — none, false, disabled, 0
# Rule F (DENY): Bash command replacing an agent settings file via cp, mv, or
# sed -i — pre-crafted file placement or in-place edit with no
# disable keywords in the command itself
#
# Bypass techniques and the macros that close them:
#
# Write/Edit tool (Rule A):
# Edit value-only diff "enabled":true→false without "sandbox" in diff
# → is_sandbox_disable_value_change ("enabled"+"false")
# Numeric zero "enabled": 0 JS falsy, not boolean false
# → is_sandbox_disable_value_zero ("enabled"+":0"/": 0")
# Null value "enabled": null JS falsy
# → is_sandbox_disable_value_null ("enabled"+"null")
# allowUnsandboxedCommands:1 numeric truthy, not string "true"
# → is_sandbox_allow_unsandboxed_numeric (":1"/": 1")
# settings.local.json same schema as settings.json, not in original path list
# → is_agent_sandbox_config extended
# Gemini "none"/"disabled" string disable values, not boolean false
# → is_sandbox_disable_gemini_none/disabled
#
# Bash tool (Rules C-F):
# echo/tee/python write Write/Edit rule never fires for Bash tool
# → Rule C: path check + content keywords
# Python "False" (capital) python3 uses False, not false; bypasses lowercase check
# → is_bash_disable_sandbox_false_pyfalse
# null in Bash command echo '{"sandbox":{"enabled":null}}' > settings
# → is_bash_disable_sandbox_null
# Bash enabled:0 echo '{"enabled":0}' > settings (no "false")
# → is_bash_disable_enabled_zero
# allowUnsandboxedCommands:1 via Bash
# → is_bash_disable_allow_unsandboxed_numeric
# sed -i settings in-place edit; no sandbox/false keyword in sed cmd
# → Rule F: is_bash_settings_sed_write
# cp/mv pre-crafted file no disable keywords at all in cp/mv command
# → Rule F: is_bash_settings_cp / is_bash_settings_mv
# Codex underscore flag --dangerously_bypass_approvals_and_sandbox
# → is_codex_bypass_flag_underscore
# GEMINI_SANDBOX=0 numeric zero not in original none/false/disabled list
# → is_gemini_env_zero
# dangerouslyDisableSandbox:false key-only check caused false positives
# → Rule B now requires both key + "true" value
#
# Known gaps (not detectable via tool call inspection):
# - Unicode escapes in JSON keys: "sandbox" parses to "sandbox" at runtime
# but tool.input contains a, not a. No decode step in Falco conditions.
# - Variable indirection in Bash: SETTINGS=$HOME/.claude/settings.json
# The variable name appears in the command, not the resolved path.
# - Two-step attack (write to /tmp then cp): the /tmp write step is caught by the
# tmp-staging rule in Section 4 before the cp step reaches Rule F.
#
# Official sandbox docs:
# Claude Code : https://code.claude.com/docs/sandboxing
# Codex : https://developers.openai.com/codex/concepts/sandboxing
# Gemini CLI : https://geminicli.com/docs/cli/sandbox/
# Macros: file path matching
# Matches agent sandbox configuration file paths (canonicalized, absolute).
# Includes settings.local.json — same schema as settings.json, equal priority.
- macro: is_agent_sandbox_config
condition: >
tool.real_file_path endswith "/.claude/settings.json"
or tool.real_file_path endswith "/.claude/settings.local.json"
or tool.real_file_path endswith "/.codex/config.toml"
or tool.real_file_path endswith "/.gemini/settings.json"
# Macros: Write/Edit content patterns (Rule A)
- macro: is_sandbox_disable_codex
condition: tool.input contains "danger-full-access"
- macro: is_sandbox_disable_enabled_false
condition: >
tool.input contains "sandbox"
and tool.input contains "false"
- macro: is_sandbox_toolsandboxing_false
condition: >
tool.input contains "toolSandboxing"
and tool.input contains "false"
- macro: is_sandbox_allow_unsandboxed
condition: >
tool.input contains "allowUnsandboxedCommands"
and tool.input contains "true"
# HARDENED: allowUnsandboxedCommands:1 — numeric truthy bypasses "true" check
- macro: is_sandbox_allow_unsandboxed_numeric
condition: >
tool.input contains "allowUnsandboxedCommands"
and (tool.input contains ":1" or tool.input contains ": 1")
# HARDENED: Edit value-only diff — "sandbox" key absent from old/new strings
- macro: is_sandbox_disable_value_change
condition: >
tool.input contains "enabled"
and tool.input contains "false"
# HARDENED: Numeric zero — JS evaluates enabled:0 as falsy
- macro: is_sandbox_disable_value_zero
condition: >
tool.input contains "enabled"
and (tool.input contains ":0" or tool.input contains ": 0")
# HARDENED: Null value — JS evaluates enabled:null as falsy
- macro: is_sandbox_disable_value_null
condition: >
tool.input contains "enabled"
and tool.input contains "null"
# HARDENED: Gemini string "none"
- macro: is_sandbox_disable_gemini_none
condition: >
tool.input contains "sandbox"
and tool.input contains "none"
# HARDENED: Gemini string "disabled"
- macro: is_sandbox_disable_gemini_disabled
condition: >
tool.input contains "sandbox"
and tool.input contains "disabled"
- macro: is_sandbox_disable_content
condition: >
is_sandbox_disable_codex
or is_sandbox_disable_enabled_false
or is_sandbox_toolsandboxing_false
or is_sandbox_allow_unsandboxed
or is_sandbox_allow_unsandboxed_numeric
or is_sandbox_disable_value_change
or is_sandbox_disable_value_zero
or is_sandbox_disable_value_null
or is_sandbox_disable_gemini_none
or is_sandbox_disable_gemini_disabled
# Macros: Bash path matching (Rules C, F)
- macro: is_bash_sandbox_settings_path
condition: >
tool.input_command contains ".claude/settings.json"
or tool.input_command contains ".claude/settings.local.json"
or tool.input_command contains ".codex/config.toml"
or tool.input_command contains ".gemini/settings.json"
# Macros: Bash content detection (Rule C)
- macro: is_bash_disable_codex
condition: tool.input_command contains "danger-full-access"
- macro: is_bash_disable_sandbox_false
condition: >
tool.input_command contains "sandbox"
and tool.input_command contains "false"
# HARDENED: Python uses capital False — bypasses lowercase "false" check
- macro: is_bash_disable_sandbox_false_pyfalse
condition: >
tool.input_command contains "sandbox"
and tool.input_command contains "False"
# HARDENED: null in Bash-written content
- macro: is_bash_disable_sandbox_null
condition: >
tool.input_command contains "sandbox"
and tool.input_command contains "null"
# HARDENED: numeric 0 for enabled field
- macro: is_bash_disable_enabled_zero
condition: >
tool.input_command contains "enabled"
and (tool.input_command contains ":0" or tool.input_command contains ": 0")
- macro: is_bash_disable_toolsandboxing_false
condition: >
tool.input_command contains "toolSandboxing"
and tool.input_command contains "false"
- macro: is_bash_disable_allow_unsandboxed
condition: >
tool.input_command contains "allowUnsandboxedCommands"
and tool.input_command contains "true"
# HARDENED: allowUnsandboxedCommands:1 via Bash
- macro: is_bash_disable_allow_unsandboxed_numeric
condition: >
tool.input_command contains "allowUnsandboxedCommands"
and (tool.input_command contains ":1" or tool.input_command contains ": 1")
- macro: is_bash_disable_sandbox_none
condition: >
tool.input_command contains "sandbox"
and tool.input_command contains "none"
- macro: is_bash_disable_sandbox_disabled
condition: >
tool.input_command contains "sandbox"
and tool.input_command contains "disabled"
- macro: is_bash_sandbox_disable_cmd
condition: >
is_bash_disable_codex
or is_bash_disable_sandbox_false
or is_bash_disable_sandbox_false_pyfalse
or is_bash_disable_sandbox_null
or is_bash_disable_enabled_zero
or is_bash_disable_toolsandboxing_false
or is_bash_disable_allow_unsandboxed
or is_bash_disable_allow_unsandboxed_numeric
or is_bash_disable_sandbox_none
or is_bash_disable_sandbox_disabled
# Macros: Bash file replacement (Rule F)
# sed -i on a settings file — no disable keywords need to appear in the sed command
- macro: is_bash_settings_sed_write
condition: >
tool.input_command contains "sed"
and tool.input_command contains "-i"
and is_bash_sandbox_settings_path
# cp of any file onto a settings path — pre-crafted payload placement
- macro: is_bash_settings_cp
condition: >
tool.input_command contains "cp "
and is_bash_sandbox_settings_path
# mv of any file onto a settings path
- macro: is_bash_settings_mv
condition: >
tool.input_command contains "mv "
and is_bash_sandbox_settings_path
- macro: is_bash_sandbox_settings_replace
condition: >
is_bash_settings_sed_write
or is_bash_settings_cp
or is_bash_settings_mv
# Macros: Codex CLI bypass flags (Rule D)
- macro: is_codex_bypass_flag
condition: tool.input_command contains "dangerously-bypass-approvals-and-sandbox"
# HARDENED: underscore variant — some CLI parsers normalize dashes to underscores
- macro: is_codex_bypass_flag_underscore
condition: tool.input_command contains "dangerously_bypass_approvals_and_sandbox"
- macro: is_codex_danger_flag
condition: >
tool.input_command contains "codex"
and tool.input_command contains "danger-full-access"
- macro: is_codex_sandbox_bypass
condition: >
is_codex_bypass_flag
or is_codex_bypass_flag_underscore
or is_codex_danger_flag
# Macros: GEMINI_SANDBOX env var bypass (Rule E)
- macro: is_gemini_env_none
condition: >
tool.input_command contains "GEMINI_SANDBOX"
and tool.input_command contains "none"
- macro: is_gemini_env_false
condition: >
tool.input_command contains "GEMINI_SANDBOX"
and tool.input_command contains "false"
- macro: is_gemini_env_disabled
condition: >
tool.input_command contains "GEMINI_SANDBOX"
and tool.input_command contains "disabled"
# HARDENED: numeric 0
- macro: is_gemini_env_zero
condition: >
tool.input_command contains "GEMINI_SANDBOX"
and (tool.input_command contains "=0" or tool.input_command contains "= 0")
- macro: is_gemini_sandbox_env_bypass
condition: >
is_gemini_env_none
or is_gemini_env_false
or is_gemini_env_disabled
or is_gemini_env_zero
# Rules
- rule: Ask before agent writing sandbox-disable configuration
desc: >
Requires user confirmation when a coding agent writes or edits its own
sandbox configuration file with content that may disable OS-level process
isolation. Covers Edit value-only diffs, numeric/null falsy values,
allowUnsandboxedCommands:1, Gemini string disable variants, and
settings.local.json. Fires at PreToolUse, before the write executes.
Asks rather than denies because the content macros (e.g., "enabled" +
"false") match common JSON shapes that legitimately appear in unrelated
settings.json edits — particularly when the Edit tool surfaces an
existing disable-shape value in old_string/new_string.
condition: >
is_write_tool
and tool.real_file_path != ""
and is_agent_sandbox_config
and is_sandbox_disable_content
output: >
Falco requires confirmation before %agent.name modifies its sandbox configuration at %tool.real_file_path
priority: WARNING
source: coding_agent
tags: [coding_agent_ask, AML.T0054_llm_jailbreak, AML.T0051_llm_prompt_injection,
mitre_t1562.001_impair_defenses_disable_or_modify_tools]
- rule: Ask before Claude Code per-command sandbox escape
desc: >
Requires user confirmation when Claude Code invokes the Bash tool with
dangerouslyDisableSandbox set to true. Hardened to check both the key name
and the "true" value — dangerouslyDisableSandbox:false no longer triggers a
false positive ask. This rule is the ONLY detection layer for this event.
condition: >
tool.name = "Bash"
and tool.input contains "dangerouslyDisableSandbox"
and tool.input contains "true"
output: >
Falco requires confirmation before Claude Code runs an unsandboxed Bash command (%tool.input_command)
priority: WARNING
source: coding_agent
tags: [coding_agent_ask, AML.T0054_llm_jailbreak, AML.T0051_llm_prompt_injection,
mitre_t1562.001_impair_defenses_disable_or_modify_tools]
- rule: Deny Bash command writing sandbox-disable content to agent settings file
desc: >
Blocks Bash commands that reference an agent settings file path combined with
sandbox-disabling content. Covers echo/tee/python3/node redirection. Hardened
against Python capital-F False, null values, numeric 0 for enabled field, and
numeric 1 for allowUnsandboxedCommands.
condition: >
tool.name = "Bash"
and is_bash_sandbox_settings_path
and is_bash_sandbox_disable_cmd
output: >
Falco blocked %agent.name from disabling its sandbox via Bash command: %tool.input_command
priority: CRITICAL
source: coding_agent
tags: [coding_agent_deny, AML.T0054_llm_jailbreak, AML.T0051_llm_prompt_injection,
mitre_t1562.001_impair_defenses_disable_or_modify_tools]
- rule: Deny Codex CLI sandbox bypass flag
desc: >
Blocks Bash invocations of the Codex CLI with flags that bypass sandbox
isolation. Covers both dash and underscore variants of
dangerously-bypass-approvals-and-sandbox, and --sandbox danger-full-access.
condition: >
tool.name = "Bash"
and is_codex_sandbox_bypass
output: >
Falco blocked %agent.name from starting Codex with a sandbox bypass flag (%tool.input_command)
priority: CRITICAL
source: coding_agent
tags: [coding_agent_deny, AML.T0054_llm_jailbreak, AML.T0051_llm_prompt_injection,
mitre_t1562.001_impair_defenses_disable_or_modify_tools]
- rule: Deny Gemini CLI sandbox disable via environment variable
desc: >
Blocks Bash commands that set GEMINI_SANDBOX to a disabling value (none,
false, disabled, 0). Covers inline assignment, exported assignment, and
numeric zero.
condition: >
tool.name = "Bash"
and is_gemini_sandbox_env_bypass
output: >
Falco blocked %agent.name from disabling Gemini sandbox via environment variable (%tool.input_command)
priority: CRITICAL
source: coding_agent
tags: [coding_agent_deny, AML.T0054_llm_jailbreak, AML.T0051_llm_prompt_injection,
mitre_t1562.001_impair_defenses_disable_or_modify_tools]
- rule: Deny Bash command replacing agent sandbox settings file
desc: >
Blocks Bash commands that replace an agent sandbox settings file via cp, mv,
or sed -i. These operations bypass Rule C because the sandbox-disabling payload
is in the source file — no disable keywords appear in the cp/mv/sed command.
A cp from /tmp signals a staged payload (also caught by the tmp-staging rule
in Section 4); sed -i indicates a content-oblivious rewrite.
condition: >
tool.name = "Bash"
and is_bash_sandbox_settings_replace
output: >
Falco blocked %agent.name from replacing an agent sandbox settings file (%tool.input_command)
priority: CRITICAL
source: coding_agent
tags: [coding_agent_deny, AML.T0054_llm_jailbreak, AML.T0051_llm_prompt_injection,
mitre_t1562.001_impair_defenses_disable_or_modify_tools]
# ===========================================================================
# Section 4 — Threats: credentials, dangerous commands, exfiltration, supply chain
# ===========================================================================
# Covers credential access (via Bash and via Read tool), dangerous shell
# commands, pipe-to-shell, encoded payloads, data exfiltration, IMDS access,
# credential archives, SSH covert tunnels, cron persistence, history wipe,
# package publish, shell startup file edits, agent instruction files outside
# the working directory, cross-agent auth file reads, untrusted-host installs,
# MCP execution from temp directories, and credential glob patterns.
# Macros — Bash threat detection
- macro: is_bash
condition: tool.name = "Bash"
- macro: references_credential
condition: >
tool.input_command contains ".aws/credentials"
or tool.input_command contains ".aws/config"
or tool.input_command contains "/.ssh/id_rsa"
or tool.input_command contains "/.ssh/id_ed25519"
or tool.input_command contains "/.ssh/id_ecdsa"
or tool.input_command contains "/.gnupg/"
or tool.input_command contains "/.netrc"
or tool.input_command contains "/.npmrc"
or tool.input_command contains "/.docker/config.json"
or tool.input_command contains "/.kube/"
or tool.input_command contains "/.azure/"
or tool.input_command contains "/.config/gcloud/"
or tool.input_command contains "/.git-credentials"
or tool.input_command contains "credentials.json"
or tool.input_command contains "AWS_SECRET_ACCESS_KEY"
or tool.input_command contains "GITHUB_TOKEN"
or tool.input_command contains "ANTHROPIC_API_KEY"
or tool.input_command contains "OPENAI_API_KEY"
- macro: is_pipe_to_shell
condition: >
tool.input_command contains "| bash"
or tool.input_command contains "|bash"
or tool.input_command contains "| sh"
or tool.input_command contains "|sh"
or tool.input_command contains "| zsh"
or tool.input_command contains "|zsh"
or tool.input_command contains "bash <("
or tool.input_command contains "sh <("
or tool.input_command contains "| /bin/bash"
or tool.input_command contains "|/bin/bash"
or tool.input_command contains "| /bin/sh"
or tool.input_command contains "|/bin/sh"
or tool.input_command contains "| /usr/bin/bash"
or tool.input_command contains "|/usr/bin/bash"
or tool.input_command contains "| /usr/bin/sh"
or tool.input_command contains "|/usr/bin/sh"
- macro: is_encoded_exec
condition: >
tool.input_command contains "base64 -d"
or tool.input_command contains "base64 --decode"
or tool.input_command contains "base64 -D"
or tool.input_command contains "openssl base64 -d"
or tool.input_command contains "xxd -r"
or tool.input_command contains "python3 -c"
or tool.input_command contains "python -c"
or tool.input_command contains "perl -e"
or tool.input_command contains "ruby -e"
or tool.input_command contains "node -e"
or tool.input_command contains "node --eval"
or tool.input_command contains "php -r"
- macro: is_curl_exfil
condition: >
(tool.input_command contains "curl " or tool.input_command contains "wget ")
and (tool.input_command contains " -d "
or tool.input_command contains " --data"
or tool.input_command contains " -F "
or tool.input_command contains " --form"
or tool.input_command contains " -T "
or tool.input_command contains " --upload-file"
or tool.input_command contains "@/tmp/"
or tool.input_command contains "@/var/"
or tool.input_command contains "| curl"
or tool.input_command contains "| wget")
# Destructive commands with high-confidence intent. Substrings here are
# specific enough to deny outright (dd if=, mkfs, > /dev/sda, init 0,
# sudo su, sudo -i — none of which appear naturally in coding workflows).
- macro: is_destructive_command
condition: >
tool.input_command contains "dd if="
or tool.input_command contains "dd of="
or tool.input_command contains "mkfs"
or tool.input_command contains "> /dev/sda"
or tool.input_command contains "init 0"
or tool.input_command contains "sudo su"
or tool.input_command contains "sudo -i"
# Likely-destructive commands whose substrings collide with legitimate
# development workflows: rm -rf is common in cleanup scripts and build
# tooling; "shutdown"/"reboot"/"halt" appear in prose, comments, and
# unrelated tool names. Ask rather than deny.
# Likely-destructive commands. shutdown/reboot/halt require care:
# - bare 'shutdown' is mostly echo prose — match only with real flags
# or via the systemd / sudo / absolute-path wrappers.
# - 'reboot' and 'halt' are valid bare commands — match them in
# command-position contexts (start of line, after `;`/`&&`) and via
# the same wrappers.
- macro: is_likely_destructive_command
condition: >
tool.input_command contains "rm -rf"
or tool.input_command contains "rm -fr"
or tool.input_command contains "rm --recursive"
or tool.input_command contains "sudo rm "
or tool.input_command startswith "shutdown -"
or tool.input_command startswith "shutdown now"
or tool.input_command startswith "shutdown +"
or tool.input_command contains "; shutdown -"
or tool.input_command contains "; shutdown now"
or tool.input_command contains "&& shutdown -"
or tool.input_command contains "&& shutdown now"
or tool.input_command contains "| shutdown -"
or tool.input_command contains "/sbin/shutdown"
or tool.input_command contains "sudo shutdown"
or tool.input_command contains "systemctl poweroff"
or tool.input_command startswith "reboot"
or tool.input_command contains "; reboot"
or tool.input_command contains "&& reboot"
or tool.input_command contains "/sbin/reboot"
or tool.input_command contains "sudo reboot"
or tool.input_command contains "systemctl reboot"
or tool.input_command startswith "halt"
or tool.input_command contains "; halt"
or tool.input_command contains "&& halt"
or tool.input_command contains "/sbin/halt"
or tool.input_command contains "sudo halt"
or tool.input_command contains "systemctl halt"
# Pure search/print commands whose substrings often collide with destructive
# patterns (e.g. `rg 'rm -rf' file`, `echo "shutdown"`). To stay safe under
# command chaining, the exemption requires the head command to be one of the
# listed read-only utilities AND the input to contain no `&&`, `||`, `;`,
# `$(`, or backticks. Pipes are allowed (the destructive substring still
# matches when piped — `xargs rm -rf` is a known gap, accepted because the
# common `rg | head` flow is the dominant case).
- macro: is_pure_search_or_print_command
condition: >
(tool.input_command startswith "rg "
or tool.input_command startswith "grep "
or tool.input_command startswith "ag "
or tool.input_command startswith "find "
or tool.input_command startswith "git grep "
or tool.input_command startswith "git log "
or tool.input_command startswith "git show "
or tool.input_command startswith "git diff"
or tool.input_command startswith "echo "
or tool.input_command startswith "printf "
or tool.input_command startswith "cat ")
and not tool.input_command contains "&&"
and not tool.input_command contains "||"
and not tool.input_command contains ";"
and not tool.input_command contains "$("
and not tool.input_command contains "`"
- macro: is_tmp_path
condition: >
tool.real_file_path startswith "/tmp/"
or tool.real_file_path startswith "/var/tmp/"
or tool.real_file_path startswith "/dev/shm/"
# Matches Read tool calls targeting credential file paths.
# Uses real_file_path (canonicalized) so symlinked paths are also covered.
# The Bash credential rule covers tool.input_command; this covers tool.real_file_path
# for direct Read calls — closing the gap where the agent bypasses Bash entirely
# by using the Read tool to exfiltrate secrets.
- macro: is_read_credential_path
condition: >
tool.name = "Read"
and tool.real_file_path != ""
and (tool.real_file_path contains "/.aws/credentials"
or tool.real_file_path contains "/.aws/config"
or tool.real_file_path contains "/.ssh/id_rsa"
or tool.real_file_path contains "/.ssh/id_ed25519"
or tool.real_file_path contains "/.ssh/id_ecdsa"
or tool.real_file_path contains "/.gnupg/"
or tool.real_file_path contains "/.netrc"
or tool.real_file_path contains "/.docker/config.json")
# Shell-based reverse shell techniques.
# is_reverse_shell_mkfifo_nc is a helper macro to avoid an (A and B) term
# inside an or-chain, which can cause Falco condition parser issues.
- macro: is_reverse_shell_mkfifo_nc
condition: >
tool.input_command contains "mkfifo"
and tool.input_command contains " nc "
- macro: is_reverse_shell
condition: >
tool.input_command contains "/dev/tcp/"
or tool.input_command contains ">&/dev/tcp"
or tool.input_command contains ">& /dev/tcp"
or tool.input_command contains "bash -i"
or tool.input_command contains "nc -e"
or tool.input_command contains "nc -c"
or tool.input_command contains "ncat --exec"
or tool.input_command contains "ncat -e"
or tool.input_command contains "socat exec:"
or is_reverse_shell_mkfifo_nc
or tool.input_command contains "exec 5<>"
# curl/wget targeting cloud instance metadata endpoints.
# Covers the GET case not caught by is_curl_exfil (which requires POST/upload flags).
# AWS IMDS 169.254.169.254 can be encoded as:
# Decimal: 2852039166 (169*16777216 + 254*65536 + 169*256 + 254)
# Hex: 0xa9fea9fe
# Octal: 0251.0376.0251.0376
# These numeric forms resolve identically in curl/wget but bypass the literal check.
- macro: is_imds_access
condition: >
(tool.input_command contains "curl " or tool.input_command contains "wget ")
and (tool.input_command contains "169.254.169.254"
or tool.input_command contains "metadata.google.internal"
or tool.input_command contains "metadata.azure.com"
or tool.input_command contains "fd00:ec2::254"
or tool.input_command contains "2852039166"
or tool.input_command contains "0xa9fea9fe"
or tool.input_command contains "0Xa9fea9fe"
or tool.input_command contains "0251.0376.0251.0376")
# Archive commands that include credential directories in their paths.
- macro: is_credential_archive
condition: >
(tool.input_command contains "tar " or tool.input_command contains "zip "
or tool.input_command contains "gzip " or tool.input_command contains "7z "
or tool.input_command contains "rar ")
and (tool.input_command contains "/.aws"
or tool.input_command contains "/.ssh"
or tool.input_command contains "/.gnupg"
or tool.input_command contains "/.kube"
or tool.input_command contains "/.docker")
# SSH port forwarding flags used to create covert channels.
# -R (remote forward) and -D (SOCKS proxy) are flagged; -L (local forward,
# common for DB tunneling) is intentionally excluded.
- macro: is_ssh_tunnel
condition: >
tool.input_command contains "ssh "
and (tool.input_command contains " -R "
or tool.input_command contains " -D "
or tool.input_command contains " -w ")
# Commands that install or modify cron jobs.
# Uses "crontab -" to avoid matching filenames containing "crontab" (e.g. grep crontab file).
- macro: is_cron_persistence
condition: >
tool.input_command contains "crontab -"
or tool.input_command contains "| crontab"
or tool.input_command contains "/etc/cron"
# Commands that wipe or suppress shell history.
# Uses specific file names (.bash_history, .zsh_history) to avoid matching
# "rm some-history-file.txt" or "grep history /etc/hosts".
# Helper macros avoid (A and B) terms inside or-chains.
- macro: is_history_wipe_rm_bash
condition: >
tool.input_command contains "rm "
and tool.input_command contains ".bash_history"
- macro: is_history_wipe_rm_zsh
condition: >
tool.input_command contains "rm "
and tool.input_command contains ".zsh_history"
- macro: is_history_wipe_truncate
condition: >
tool.input_command contains "truncate "
and tool.input_command contains "_history"
- macro: is_history_wipe
condition: >
tool.input_command contains "history -c"
or tool.input_command contains "history -w"
or tool.input_command contains "unset HISTFILE"
or tool.input_command contains "HISTSIZE=0"
or tool.input_command contains "HISTFILESIZE=0"
or is_history_wipe_rm_bash
or is_history_wipe_rm_zsh
or is_history_wipe_truncate
# Package registry publish commands across major ecosystems.
- macro: is_package_publish
condition: >
tool.input_command contains "npm publish"
or tool.input_command contains "twine upload"
or tool.input_command contains "cargo publish"
or tool.input_command contains "gem push"
or tool.input_command contains "pip upload"
# Reading another agent's OAuth or session credential files.
# Each agent should only read its own credential store. The negation
# (agent.name != X) excludes the owning agent from the deny.
# Helper macros avoid (A and B) terms inside or-chains in is_cross_agent_auth_read.
- macro: is_gemini_auth_cross
condition: >
tool.real_file_path contains "/.gemini/oauth_creds"
and agent.name != "gemini"
- macro: is_codex_auth_cross
condition: >
tool.real_file_path contains "/.codex/auth"
and agent.name != "codex"
- macro: is_cursor_auth_cross
condition: >
tool.real_file_path contains "/.cursor/session"
and agent.name != "cursor"
- macro: is_claude_auth_cross
condition: >
tool.real_file_path contains "/.claude/oauth"
and agent.name != "claude_code"
- macro: is_cross_agent_auth_read
condition: >
tool.name = "Read"
and (is_gemini_auth_cross
or is_codex_auth_cross
or is_cursor_auth_cross
or is_claude_auth_cross)
# Package/skill install commands referencing known malicious code hosts.