farai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,117 @@
1
+ ARG GO_VERSION=1.25.0
2
+ ARG GOPLS_VERSION=v0.20.0
3
+ ARG RUST_ANALYZER_VERSION=2025-08-25
4
+
5
+ FROM golang:${GO_VERSION}-bookworm AS lsp-builder
6
+ ARG GOPLS_VERSION
7
+ ARG RUST_ANALYZER_VERSION
8
+ ARG TARGETARCH
9
+ RUN mkdir -p /out \
10
+ && GOBIN=/out go install golang.org/x/tools/gopls@${GOPLS_VERSION}
11
+
12
+ RUN set -eux; \
13
+ case "${TARGETARCH:-amd64}" in \
14
+ amd64) rust_analyzer_arch=x86_64; rust_analyzer_sha=487fb1cb99e567fd6b818cc88a7c1452d5260da57dbc171fc1359a4b604348ae ;; \
15
+ arm64) rust_analyzer_arch=aarch64; rust_analyzer_sha=c738e00a5caa712f9d9c8f81f9661884c7108ef94db6df48985c11fe3c2695bd ;; \
16
+ *) echo "unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
17
+ esac; \
18
+ curl -fsSL "https://github.com/rust-lang/rust-analyzer/releases/download/${RUST_ANALYZER_VERSION}/rust-analyzer-${rust_analyzer_arch}-unknown-linux-gnu.gz" -o /tmp/rust-analyzer.gz; \
19
+ echo "${rust_analyzer_sha} /tmp/rust-analyzer.gz" | sha256sum -c -; \
20
+ gzip -dc /tmp/rust-analyzer.gz >/out/rust-analyzer; \
21
+ chmod 0755 /out/rust-analyzer; \
22
+ /out/rust-analyzer --version; \
23
+ rm -f /tmp/rust-analyzer.gz
24
+
25
+ FROM kalilinux/kali-rolling
26
+
27
+ ENV DEBIAN_FRONTEND=noninteractive
28
+
29
+ ARG TARGETARCH
30
+ ARG NAABU_VERSION=2.6.1
31
+ ARG INTERACTSH_VERSION=1.3.1
32
+ ARG TYPESCRIPT_VERSION=5.9.3
33
+ ARG TYPESCRIPT_LANGUAGE_SERVER_VERSION=4.3.4
34
+ ARG PYRIGHT_VERSION=1.1.408
35
+
36
+ LABEL org.farai.kali.contract="curated-security-runtime"
37
+
38
+ COPY farai-tool-manifest.json /usr/local/share/farai/farai-tool-manifest.json
39
+
40
+ RUN apt-get update \
41
+ && apt-get install -y --no-install-recommends python3-minimal \
42
+ && python3 -c 'import json, subprocess; manifest=json.load(open("/usr/local/share/farai/farai-tool-manifest.json", encoding="utf-8")); subprocess.run(["apt-get", "install", "-y", "--no-install-recommends", *manifest["aptPackages"]], check=True)' \
43
+ && apt-get clean \
44
+ && rm -rf /var/lib/apt/lists/*
45
+
46
+ RUN set -eux; \
47
+ case "${TARGETARCH:-amd64}" in \
48
+ amd64) \
49
+ naabu_arch=amd64; naabu_sha=018c4c9884dea971eda860435ede3021d1150732f34cfd245498c6726d8cab90; \
50
+ interactsh_arch=amd64; interactsh_sha=d553fb3dd4c2684c953aedeb555f13f0d1ca94bd75112c949a5fd975d385a903 ;; \
51
+ arm64) \
52
+ naabu_arch=arm64; naabu_sha=3adc2bb2395c3efff89623499b20eea66ef54924c485d3ae86762393a31736ea; \
53
+ interactsh_arch=arm64; interactsh_sha=475aa78c59afad115149a5b0d6d9c99d2410a4f12d0c6f5a24c32e7338fc6adb ;; \
54
+ *) echo "unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
55
+ esac; \
56
+ curl -fsSL "https://github.com/projectdiscovery/naabu/releases/download/v${NAABU_VERSION}/naabu_${NAABU_VERSION}_linux_${naabu_arch}.zip" -o /tmp/naabu.zip; \
57
+ echo "${naabu_sha} /tmp/naabu.zip" | sha256sum -c -; \
58
+ unzip -q /tmp/naabu.zip -d /tmp/naabu; \
59
+ install -m 0755 /tmp/naabu/naabu /usr/local/bin/naabu; \
60
+ curl -fsSL "https://github.com/projectdiscovery/interactsh/releases/download/v${INTERACTSH_VERSION}/interactsh-client_${INTERACTSH_VERSION}_linux_${interactsh_arch}.zip" -o /tmp/interactsh.zip; \
61
+ echo "${interactsh_sha} /tmp/interactsh.zip" | sha256sum -c -; \
62
+ unzip -q /tmp/interactsh.zip -d /tmp/interactsh; \
63
+ install -m 0755 /tmp/interactsh/interactsh-client /usr/local/bin/interactsh-client; \
64
+ rm -rf /tmp/naabu /tmp/interactsh /tmp/naabu.zip /tmp/interactsh.zip
65
+
66
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh \
67
+ && ln -sf /root/.local/bin/uv /usr/local/bin/uv \
68
+ && ln -sf /root/.local/bin/uvx /usr/local/bin/uvx
69
+
70
+ RUN UV_TOOL_DIR=/opt/uv/tools UV_TOOL_BIN_DIR=/opt/uv/bin uv tool install --with fastmcp mitmproxy-mcp==0.6.1 \
71
+ && chmod -R a+rX /opt/uv \
72
+ && PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install -g \
73
+ @playwright/mcp@latest \
74
+ typescript@${TYPESCRIPT_VERSION} \
75
+ typescript-language-server@${TYPESCRIPT_LANGUAGE_SERVER_VERSION} \
76
+ pyright@${PYRIGHT_VERSION} \
77
+ && uv cache clean \
78
+ && npm cache clean --force
79
+
80
+ COPY --from=lsp-builder /out/gopls /usr/local/bin/gopls
81
+ COPY --from=lsp-builder /out/rust-analyzer /usr/local/bin/rust-analyzer
82
+
83
+ RUN useradd --system --create-home --home-dir /home/mitm --shell /usr/sbin/nologin mitm \
84
+ && mkdir -p /home/mitm/.mitmproxy /usr/local/share/farai \
85
+ && openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
86
+ -keyout /tmp/farai-ca-key.pem -out /tmp/farai-ca-cert.pem \
87
+ -subj "/CN=farai mitmproxy/O=farai" \
88
+ -addext "basicConstraints=critical,CA:TRUE" \
89
+ -addext "keyUsage=critical,keyCertSign,cRLSign" \
90
+ && cat /tmp/farai-ca-key.pem /tmp/farai-ca-cert.pem >/home/mitm/.mitmproxy/mitmproxy-ca.pem \
91
+ && cp /tmp/farai-ca-cert.pem /home/mitm/.mitmproxy/mitmproxy-ca-cert.pem \
92
+ && cp /tmp/farai-ca-cert.pem /usr/local/share/ca-certificates/farai-mitmproxy.crt \
93
+ && cp /tmp/farai-ca-cert.pem /usr/local/share/farai/farai-mitmproxy-ca.pem \
94
+ && rm -f /tmp/farai-ca-key.pem /tmp/farai-ca-cert.pem \
95
+ && chown -R mitm:mitm /home/mitm \
96
+ && chmod 700 /home/mitm/.mitmproxy \
97
+ && update-ca-certificates \
98
+ && mkdir -p /root/.pki/nssdb \
99
+ && certutil -d sql:/root/.pki/nssdb -N --empty-password \
100
+ && certutil -d sql:/root/.pki/nssdb -A -n farai-mitmproxy -t "C,," -i /usr/local/share/farai/farai-mitmproxy-ca.pem
101
+
102
+ COPY redsocks.conf.tmpl /usr/local/share/farai/redsocks.conf.tmpl
103
+ COPY farai-proxy-init.sh /usr/local/bin/farai-proxy-init
104
+ COPY farai-proxy-teardown.sh /usr/local/bin/farai-proxy-teardown
105
+ COPY farai-mitmproxy-mcp.sh /usr/local/bin/mitmproxy-mcp
106
+ COPY farai_mitmproxy_mcp.py /usr/local/share/farai/farai_mitmproxy_mcp.py
107
+ COPY farai-image-doctor.py /usr/local/bin/farai-image-doctor
108
+ RUN chmod 0755 /usr/local/bin/farai-proxy-init /usr/local/bin/farai-proxy-teardown /usr/local/bin/mitmproxy-mcp /usr/local/bin/farai-image-doctor \
109
+ && farai-image-doctor --write /usr/local/share/farai/kali-capabilities.json
110
+
111
+ ENV NODE_EXTRA_CA_CERTS=/usr/local/share/farai/farai-mitmproxy-ca.pem \
112
+ REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
113
+ SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
114
+ CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
115
+ GIT_SSL_CAINFO=/etc/ssl/certs/ca-certificates.crt
116
+
117
+ WORKDIR /workspace
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import os
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+
8
+
9
+ def resolve_manifest_path():
10
+ configured = os.environ.get("FARAI_TOOL_MANIFEST")
11
+ candidates = [
12
+ Path(configured) if configured else None,
13
+ Path("/usr/local/share/farai/farai-tool-manifest.json"),
14
+ Path(__file__).with_name("farai-tool-manifest.json"),
15
+ ]
16
+ for candidate in candidates:
17
+ if candidate is not None and candidate.is_file():
18
+ return candidate
19
+ raise FileNotFoundError("farai tool manifest not found")
20
+
21
+
22
+ MANIFEST_PATH = resolve_manifest_path()
23
+ MANIFEST = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
24
+ CONTRACT = MANIFEST["contract"]
25
+ APT_PACKAGES = tuple(MANIFEST["aptPackages"])
26
+ WORKFLOWS = MANIFEST["workflows"]
27
+ REQUIRED = tuple(dict.fromkeys(command for commands in WORKFLOWS.values() for command in commands))
28
+
29
+
30
+ def installed_commands():
31
+ commands = {}
32
+ for directory in os.environ.get("PATH", "").split(os.pathsep):
33
+ resolved = os.path.realpath(directory)
34
+ if not resolved or not os.path.isdir(resolved):
35
+ continue
36
+ try:
37
+ names = os.listdir(resolved)
38
+ except OSError:
39
+ continue
40
+ for name in names:
41
+ path = os.path.join(resolved, name)
42
+ if name not in commands and os.path.isfile(path) and os.access(path, os.X_OK):
43
+ commands[name] = path
44
+ return commands
45
+
46
+
47
+ def missing_packages():
48
+ missing = []
49
+ for package in APT_PACKAGES:
50
+ result = subprocess.run(
51
+ ["dpkg-query", "-W", "-f=${Status}", package],
52
+ capture_output=True,
53
+ text=True,
54
+ check=False,
55
+ )
56
+ if result.returncode != 0 or result.stdout.strip() != "install ok installed":
57
+ missing.append(package)
58
+ return missing
59
+
60
+
61
+ commands = installed_commands()
62
+ missing = [name for name in REQUIRED if name not in commands]
63
+ missing_apt_packages = missing_packages()
64
+ payload = {
65
+ "contract": CONTRACT,
66
+ "manifest": str(MANIFEST_PATH),
67
+ "inventoryCount": len(commands),
68
+ "packageCount": len(APT_PACKAGES),
69
+ "requiredCount": len(REQUIRED),
70
+ "missing": missing,
71
+ "missingPackages": missing_apt_packages,
72
+ "workflows": WORKFLOWS,
73
+ }
74
+
75
+ if len(sys.argv) == 3 and sys.argv[1] == "--write":
76
+ payload["commands"] = sorted(commands)
77
+ destination = Path(sys.argv[2])
78
+ destination.parent.mkdir(parents=True, exist_ok=True)
79
+ destination.write_text(json.dumps(payload, ensure_ascii=True, separators=(",", ":")) + "\n", encoding="utf-8")
80
+ else:
81
+ print(json.dumps(payload, ensure_ascii=True, separators=(",", ":")))
82
+
83
+ raise SystemExit(0 if not missing and not missing_apt_packages else 1)
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ proxy_user="${FARAI_PROXY_USER:-mitm}"
5
+ chown "$proxy_user:$proxy_user" "$PWD" 2>/dev/null || true
6
+
7
+ exec setpriv --reuid="$proxy_user" --regid="$proxy_user" --init-groups \
8
+ env HOME=/home/mitm PATH=/opt/uv/bin:/usr/local/bin:/usr/bin:/bin \
9
+ /opt/uv/tools/mitmproxy-mcp/bin/python /usr/local/share/farai/farai_mitmproxy_mcp.py "$@"
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ proxy_port="${FARAI_PROXY_PORT:-31337}"
5
+ redsocks_port="${FARAI_REDSOCKS_PORT:-31338}"
6
+ tcp_ports="${FARAI_PROXY_TCP_PORTS:-80,443,3000,5000,8000,8008,8080,8081,8443,8888,9000}"
7
+ proxy_user="${FARAI_PROXY_USER:-mitm}"
8
+ runtime_dir=/run/farai
9
+ chain=FARAI_PROXY
10
+
11
+ mkdir -p "$runtime_dir"
12
+
13
+ proxy_listening() {
14
+ (exec 3<>"/dev/tcp/127.0.0.1/$proxy_port") 2>/dev/null || return 1
15
+ exec 3>&- 3<&-
16
+ return 0
17
+ }
18
+
19
+ attempts=0
20
+ until proxy_listening; do
21
+ attempts=$((attempts + 1))
22
+ if [ "$attempts" -ge 60 ]; then
23
+ echo "farai-proxy-init: no proxy listening on 127.0.0.1:$proxy_port" >&2
24
+ exit 1
25
+ fi
26
+ sleep 0.5
27
+ done
28
+
29
+ if ! [ -s "$runtime_dir/redsocks.pid" ] || ! kill -0 "$(cat "$runtime_dir/redsocks.pid")" 2>/dev/null; then
30
+ sed -e "s/__LOCAL_PORT__/$redsocks_port/" -e "s/__PROXY_PORT__/$proxy_port/" \
31
+ /usr/local/share/farai/redsocks.conf.tmpl >"$runtime_dir/redsocks.conf"
32
+ redsocks -c "$runtime_dir/redsocks.conf" -p "$runtime_dir/redsocks.pid"
33
+ fi
34
+
35
+ iptables -t nat -N "$chain" 2>/dev/null || true
36
+ iptables -t nat -F "$chain"
37
+ iptables -t nat -A "$chain" -d 127.0.0.0/8 -j RETURN
38
+ iptables -t nat -A "$chain" -m owner --uid-owner "$proxy_user" -j RETURN
39
+ iptables -t nat -A "$chain" -p tcp -m multiport --dports "$tcp_ports" -j REDIRECT --to-ports "$redsocks_port"
40
+ iptables -t nat -C OUTPUT -p tcp -j "$chain" 2>/dev/null || iptables -t nat -A OUTPUT -p tcp -j "$chain"
41
+ iptables -C OUTPUT -p udp --dport 443 -j REJECT 2>/dev/null || iptables -A OUTPUT -p udp --dport 443 -j REJECT
42
+
43
+ echo "farai-proxy-init: transparent capture active on tcp $tcp_ports via 127.0.0.1:$proxy_port"
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+ set -uo pipefail
3
+
4
+ chain=FARAI_PROXY
5
+ runtime_dir=/run/farai
6
+
7
+ iptables -t nat -D OUTPUT -p tcp -j "$chain" 2>/dev/null || true
8
+ iptables -t nat -F "$chain" 2>/dev/null || true
9
+ iptables -t nat -X "$chain" 2>/dev/null || true
10
+ iptables -D OUTPUT -p udp --dport 443 -j REJECT 2>/dev/null || true
11
+
12
+ if [ -s "$runtime_dir/redsocks.pid" ]; then
13
+ kill "$(cat "$runtime_dir/redsocks.pid")" 2>/dev/null || true
14
+ rm -f "$runtime_dir/redsocks.pid"
15
+ fi
16
+
17
+ echo "farai-proxy-init: transparent capture disabled"
@@ -0,0 +1,215 @@
1
+ {
2
+ "contract": "curated-security-runtime",
3
+ "aptPackages": [
4
+ "amass",
5
+ "arjun",
6
+ "assetfinder",
7
+ "binwalk",
8
+ "bind9-dnsutils",
9
+ "build-essential",
10
+ "ca-certificates",
11
+ "cargo",
12
+ "chromium",
13
+ "commix",
14
+ "curl",
15
+ "dirb",
16
+ "dirsearch",
17
+ "dnsenum",
18
+ "dnsrecon",
19
+ "dnsx",
20
+ "enum4linux-ng",
21
+ "evil-winrm",
22
+ "exploitdb",
23
+ "feroxbuster",
24
+ "ffuf",
25
+ "file",
26
+ "findomain",
27
+ "fonts-liberation",
28
+ "foremost",
29
+ "gdb",
30
+ "git",
31
+ "gobuster",
32
+ "golang-go",
33
+ "hashcat",
34
+ "hydra",
35
+ "impacket-scripts",
36
+ "iproute2",
37
+ "iptables",
38
+ "iputils-ping",
39
+ "john",
40
+ "jq",
41
+ "less",
42
+ "libimage-exiftool-perl",
43
+ "libnss3-tools",
44
+ "libpcap0.8t64",
45
+ "masscan",
46
+ "massdns",
47
+ "medusa",
48
+ "mitmproxy",
49
+ "ncrack",
50
+ "netcat-traditional",
51
+ "netexec",
52
+ "nikto",
53
+ "nmap",
54
+ "nodejs",
55
+ "npm",
56
+ "nuclei",
57
+ "openssh-client",
58
+ "openssl",
59
+ "pipx",
60
+ "procps",
61
+ "python3",
62
+ "python3-pip",
63
+ "python3-venv",
64
+ "radare2",
65
+ "redsocks",
66
+ "responder",
67
+ "ripgrep",
68
+ "rustc",
69
+ "smbclient",
70
+ "smbmap",
71
+ "socat",
72
+ "sqlmap",
73
+ "sslscan",
74
+ "sslyze",
75
+ "steghide",
76
+ "subfinder",
77
+ "tcpdump",
78
+ "testssl.sh",
79
+ "theharvester",
80
+ "tree",
81
+ "tshark",
82
+ "unzip",
83
+ "util-linux",
84
+ "wafw00f",
85
+ "wapiti",
86
+ "wget",
87
+ "wfuzz",
88
+ "whatweb",
89
+ "whois",
90
+ "wordlists",
91
+ "wpscan",
92
+ "xz-utils",
93
+ "yara",
94
+ "zip"
95
+ ],
96
+ "workflows": {
97
+ "shell-and-network-utilities": [
98
+ "curl",
99
+ "dig",
100
+ "file",
101
+ "ip",
102
+ "iptables",
103
+ "jq",
104
+ "nc",
105
+ "openssl",
106
+ "ping",
107
+ "rg",
108
+ "ssh",
109
+ "unzip",
110
+ "wget",
111
+ "whois",
112
+ "zip"
113
+ ],
114
+ "browser-and-proxy": [
115
+ "chromium",
116
+ "mitmdump",
117
+ "mitmproxy",
118
+ "mitmproxy-mcp",
119
+ "playwright-mcp"
120
+ ],
121
+ "passive-asset-discovery": [
122
+ "amass",
123
+ "assetfinder",
124
+ "dnsenum",
125
+ "dnsrecon",
126
+ "dnsx",
127
+ "findomain",
128
+ "massdns",
129
+ "subfinder",
130
+ "theHarvester"
131
+ ],
132
+ "service-discovery": [
133
+ "masscan",
134
+ "naabu",
135
+ "nmap"
136
+ ],
137
+ "callback-verification": [
138
+ "interactsh-client",
139
+ "socat"
140
+ ],
141
+ "web-content-discovery": [
142
+ "arjun",
143
+ "dirb",
144
+ "dirsearch",
145
+ "feroxbuster",
146
+ "ffuf",
147
+ "gobuster"
148
+ ],
149
+ "web-assessment": [
150
+ "commix",
151
+ "nikto",
152
+ "nuclei",
153
+ "sqlmap",
154
+ "sslscan",
155
+ "sslyze",
156
+ "testssl",
157
+ "wafw00f",
158
+ "wapiti",
159
+ "wfuzz",
160
+ "whatweb",
161
+ "wpscan"
162
+ ],
163
+ "credential-access": [
164
+ "hashcat",
165
+ "hydra",
166
+ "john",
167
+ "medusa",
168
+ "ncrack"
169
+ ],
170
+ "exploit-reference": [
171
+ "searchsploit"
172
+ ],
173
+ "active-directory": [
174
+ "enum4linux-ng",
175
+ "evil-winrm",
176
+ "impacket-secretsdump",
177
+ "netexec",
178
+ "responder",
179
+ "smbclient",
180
+ "smbmap"
181
+ ],
182
+ "reverse-engineering-and-forensics": [
183
+ "binwalk",
184
+ "exiftool",
185
+ "foremost",
186
+ "gdb",
187
+ "r2",
188
+ "radare2",
189
+ "steghide",
190
+ "yara"
191
+ ],
192
+ "network-observation": [
193
+ "socat",
194
+ "tcpdump",
195
+ "tshark"
196
+ ],
197
+ "development": [
198
+ "cargo",
199
+ "git",
200
+ "go",
201
+ "gopls",
202
+ "node",
203
+ "npm",
204
+ "pip3",
205
+ "pyright",
206
+ "python3",
207
+ "rg",
208
+ "rust-analyzer",
209
+ "rustc",
210
+ "typescript-language-server",
211
+ "uv",
212
+ "uvx"
213
+ ]
214
+ }
215
+ }