Skip to content

Commit f8253e6

Browse files
committed
feat(rules): ask before container privilege escalation or host breakout
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
1 parent 2f6227a commit f8253e6

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

rules/default/coding_agents_rules.yaml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,102 @@
10261026
or tool.input contains "/id_ed25519"
10271027
or tool.input contains "oauth_creds")
10281028
1029+
# Container privilege-escalation and host-breakout patterns in container-run
1030+
# commands. The flags below are shared by docker, podman, and nerdctl, so the
1031+
# substring matches are runtime-agnostic and cover run/create/exec alike.
1032+
# Each is a documented route to host-equivalent power or container escape —
1033+
# for a coding agent, a clean way out of whatever containment Prempti is meant
1034+
# to complement, and one Prempti cannot observe after the fact (it only sees
1035+
# what the agent declares at the hook layer). Ask rather than deny: dind,
1036+
# devcontainers, and GPU/CI workloads use several of these flags legitimately.
1037+
# Helpers are split into separate macros to keep the umbrella readable and to
1038+
# avoid (A and B) terms nested inside an or-chain.
1039+
1040+
# --privileged on run/create/exec — full host device + capability access.
1041+
- macro: is_container_privileged_flag
1042+
condition: tool.input_command contains "--privileged"
1043+
1044+
# Added Linux capabilities (--cap-add=SYS_ADMIN, =ALL, =SYS_PTRACE, …).
1045+
- macro: is_container_cap_add
1046+
condition: tool.input_command contains "--cap-add"
1047+
1048+
# Disabled seccomp/AppArmor/SELinux profiles or unconfined system paths.
1049+
# Deliberately matches the dangerous values, not bare --security-opt, so the
1050+
# hardening value --security-opt no-new-privileges does not trigger an ask.
1051+
- macro: is_container_security_opt_unconfined
1052+
condition: >
1053+
tool.input_command contains "seccomp=unconfined"
1054+
or tool.input_command contains "apparmor=unconfined"
1055+
or tool.input_command contains "systempaths=unconfined"
1056+
or tool.input_command contains "label=disable"
1057+
or tool.input_command contains "label:disable"
1058+
1059+
# Shared host namespaces, both = and space separated. --pid=host enables
1060+
# nsenter into host PID 1; --ipc/--userns/--uts/--cgroupns=host weaken
1061+
# isolation; --net/--network=host exposes host-bound services (the most
1062+
# common of these in benign dev — relax via override first if it nags).
1063+
- macro: is_container_host_namespace
1064+
condition: >
1065+
tool.input_command contains "--pid=host"
1066+
or tool.input_command contains "--pid host"
1067+
or tool.input_command contains "--ipc=host"
1068+
or tool.input_command contains "--ipc host"
1069+
or tool.input_command contains "--userns=host"
1070+
or tool.input_command contains "--userns host"
1071+
or tool.input_command contains "--uts=host"
1072+
or tool.input_command contains "--uts host"
1073+
or tool.input_command contains "--cgroupns=host"
1074+
or tool.input_command contains "--cgroupns host"
1075+
or tool.input_command contains "--network=host"
1076+
or tool.input_command contains "--network host"
1077+
or tool.input_command contains "--net=host"
1078+
or tool.input_command contains "--net host"
1079+
1080+
# Docker daemon socket exposed to or targeted by the command — a container
1081+
# with the socket mounted can spawn a privileged sibling and own the host;
1082+
# DOCKER_HOST= points the client at an arbitrary daemon.
1083+
- macro: is_container_docker_socket
1084+
condition: >
1085+
tool.input_command contains "docker.sock"
1086+
or tool.input_command contains "DOCKER_HOST="
1087+
1088+
# Host root filesystem bind-mounted into the container (-v /:… or
1089+
# --mount source=/,…) — direct read/write of the entire host.
1090+
- macro: is_container_host_root_mount
1091+
condition: >
1092+
tool.input_command contains "-v /:"
1093+
or tool.input_command contains "--volume /:"
1094+
or tool.input_command contains "source=/,"
1095+
or tool.input_command contains "src=/,"
1096+
1097+
# Raw host device passthrough (--device=/dev/…). Covers /dev/mem, raw disks,
1098+
# etc.; also matches benign GPU/USB passthrough, hence ask not deny.
1099+
- macro: is_container_device_passthrough
1100+
condition: >
1101+
tool.input_command contains "--device=/dev/"
1102+
or tool.input_command contains "--device /dev/"
1103+
1104+
# nsenter into the host's namespaces via init (PID 1) — the standard breakout
1105+
# once --pid=host or the docker socket is available. Gated on targeting PID 1
1106+
# so the word nsenter alone in prose does not match.
1107+
- macro: is_nsenter_host
1108+
condition: >
1109+
tool.input_command contains "nsenter"
1110+
and (tool.input_command contains "-t 1"
1111+
or tool.input_command contains "--target 1"
1112+
or tool.input_command contains "-t1")
1113+
1114+
- macro: is_container_privilege_escalation
1115+
condition: >
1116+
is_container_privileged_flag
1117+
or is_container_cap_add
1118+
or is_container_security_opt_unconfined
1119+
or is_container_host_namespace
1120+
or is_container_docker_socket
1121+
or is_container_host_root_mount
1122+
or is_container_device_passthrough
1123+
or is_nsenter_host
1124+
10291125
# Rules
10301126

10311127
- rule: Deny credential file access via Bash
@@ -1087,6 +1183,28 @@
10871183
source: coding_agent
10881184
tags: [coding_agent_ask]
10891185

1186+
- rule: Ask before container privilege escalation or host breakout
1187+
desc: >
1188+
Requires user confirmation before Bash commands that grant a container
1189+
host-equivalent power or break out of container isolation: --privileged,
1190+
--cap-add, disabled seccomp/AppArmor/SELinux profiles (--security-opt
1191+
*=unconfined or label=disable), shared host namespaces (--pid / --ipc /
1192+
--userns / --uts / --cgroupns / --network = host), Docker daemon socket
1193+
exposure (docker.sock, DOCKER_HOST=), host root bind mounts (-v /: or
1194+
--mount source=/), raw device passthrough (--device=/dev/), and nsenter
1195+
into host PID 1. The flags are shared by docker, podman, and nerdctl.
1196+
Prempti runs in user space and only sees what the agent declares at the
1197+
hook layer, so a privileged container is a route to host access it cannot
1198+
observe afterward. Asks rather than denies because dind, devcontainers,
1199+
and GPU/CI workloads use these flags legitimately; --network=host is the
1200+
most common of these in benign dev and is the first to relax via override.
1201+
condition: is_bash and is_container_privilege_escalation
1202+
output: >
1203+
Falco requires confirmation before %agent.name runs a container command that escalates privileges or breaks out of container isolation (%tool.input_command)
1204+
priority: WARNING
1205+
source: coding_agent
1206+
tags: [coding_agent_ask, mitre_t1611_escape_to_host, mitre_t1610_deploy_container]
1207+
10901208
- rule: Deny pipe to shell interpreter
10911209
desc: >
10921210
Blocks Bash commands that pipe network-fetched or generated content directly

0 commit comments

Comments
 (0)