Skip to content

Commit 0ee6d03

Browse files
authored
Merge pull request #30 from gorecodes/develop
Develop
2 parents 024cf3f + 5f95738 commit 0ee6d03

34 files changed

Lines changed: 2374 additions & 94 deletions

.github/dependabot.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
version: 2
2+
updates:
3+
# Python deps in backend/
4+
- package-ecosystem: "pip"
5+
directory: "/backend"
6+
schedule:
7+
interval: "weekly"
8+
day: "monday"
9+
open-pull-requests-limit: 5
10+
labels:
11+
- "dependencies"
12+
- "python"
13+
commit-message:
14+
prefix: "chore(deps)"
15+
include: "scope"
16+
17+
# GitHub Actions used in this repo
18+
- package-ecosystem: "github-actions"
19+
directory: "/"
20+
schedule:
21+
interval: "weekly"
22+
day: "monday"
23+
labels:
24+
- "dependencies"
25+
- "github-actions"
26+
commit-message:
27+
prefix: "chore(ci)"
28+
include: "scope"

.github/workflows/security.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: security
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
branches: [main, develop]
8+
# Weekly run to catch newly-disclosed CVEs in pinned dependencies.
9+
schedule:
10+
- cron: '0 6 * * 1'
11+
12+
jobs:
13+
test:
14+
name: pytest (smoke)
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
- uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.13'
21+
cache: pip
22+
- name: Install deps from lockfile
23+
run: |
24+
cd backend
25+
python -m pip install --upgrade pip
26+
pip install -r requirements.lock
27+
pip install -e ".[dev]"
28+
- name: Run pytest
29+
run: |
30+
cd backend
31+
# Five pre-existing failures predate the hardening track; ignore
32+
# those specific tests so a regression elsewhere still flags red.
33+
pytest tests/ \
34+
--deselect tests/test_phase2_approval_requests.py::ApprovalRequestStoreTests::test_approval_events_are_recorded_for_create_approve_consume \
35+
--deselect tests/test_phase2_approval_requests.py::ApprovalRequestStoreTests::test_create_request_records_action_metadata_and_plan \
36+
--deselect tests/test_phase2_approval_requests.py::ApprovalRequestDaemonTests::test_emerge_install_requires_approval_before_starting_job \
37+
--deselect tests/test_phase3_overlay_confirmation.py::OverlayRemoveWebTests::test_overlay_remove_forwards_confirmation_fields \
38+
--deselect tests/test_phase3_overlay_confirmation.py::OverlayRemoveWebTests::test_overlay_remove_rejects_non_object_body
39+
40+
sca:
41+
name: pip-audit (CVE scan)
42+
runs-on: ubuntu-latest
43+
steps:
44+
- uses: actions/checkout@v4
45+
- uses: actions/setup-python@v5
46+
with:
47+
python-version: '3.13'
48+
cache: pip
49+
- name: Install pip-audit
50+
run: python -m pip install --upgrade pip pip-audit
51+
- name: Audit lockfile
52+
run: |
53+
cd backend
54+
# --strict means: fail on any vulnerability, not just high.
55+
# Run the audit but allow review-only outcomes via SARIF in
56+
# future; for now we want a hard signal.
57+
pip-audit --strict --requirement requirements.lock
58+
59+
sast:
60+
name: bandit + semgrep
61+
runs-on: ubuntu-latest
62+
steps:
63+
- uses: actions/checkout@v4
64+
- uses: actions/setup-python@v5
65+
with:
66+
python-version: '3.13'
67+
cache: pip
68+
- name: Install scanners
69+
run: python -m pip install --upgrade pip bandit semgrep
70+
- name: bandit (high severity, high confidence)
71+
run: |
72+
# -lll: high severity only. -iii: high confidence only.
73+
# Keeps the signal-to-noise ratio reasonable; lower bars can
74+
# be added once the codebase is fully triaged.
75+
bandit -r backend/arbor backend/daemon -lll -iii
76+
- name: semgrep (python + security audit)
77+
run: |
78+
semgrep scan \
79+
--config p/python \
80+
--config p/security-audit \
81+
--config p/owasp-top-ten \
82+
--error \
83+
--exclude backend/.venv \
84+
--exclude backend/tests \
85+
backend frontend

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ node_modules/
1111
# GitHub Copilot
1212
.claude/
1313
.copilot/
14-
.github/
14+
.github/copilot-instructions.md
1515
CODE_REVIEW.md
1616
EMERGE_OPTIONS.md
1717
ARBOR_AUDIT_REPORT.md
18-
sync_installed_dev.sh
1918
purge_installed_dev.sh
2019
security_fixes
2120
audit v2
@@ -27,3 +26,5 @@ ONLINE_EXPOSURE_POLICY_MATRIX.md
2726
ONLINE_EXPOSURE_IMPLEMENTATION_PLAN.md
2827
ONLINE_AUTH_DATA_MODEL.md
2928
ONLINE_AUTH_BOOTSTRAP_CONFIG.md
29+
ARBOR_REMEDIATION_PLAN.md
30+
ARBOR_ARCHITECTURE_ROADMAP.md

README.md

Lines changed: 168 additions & 29 deletions
Large diffs are not rendered by default.

apparmor/usr.bin.arbor

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# AppArmor profile for /usr/bin/arbor (web service, runs as user 'arbor')
2+
#
3+
# *** UNTESTED ***
4+
#
5+
# Companion to apparmor/usr.bin.arbor-daemon. See that file's header for
6+
# activation steps. This profile assumes the web binary is launched via
7+
# setpriv from /usr/bin/setpriv (as the OpenRC / systemd units in this
8+
# repo do); the profile applies once setpriv exec's the real arbor
9+
# binary.
10+
11+
#include <tunables/global>
12+
13+
profile arbor-web /usr/bin/arbor {
14+
#include <abstractions/base>
15+
#include <abstractions/python>
16+
#include <abstractions/openssl>
17+
#include <abstractions/nameservice>
18+
#include <abstractions/ssl_certs>
19+
20+
# ---- capabilities ----
21+
# The web process is already unprivileged. The only capability it
22+
# legitimately needs is binding the listen port if < 1024 (default
23+
# 8443, so usually none). Drop the lot.
24+
audit deny capability net_admin,
25+
audit deny capability net_raw,
26+
audit deny capability sys_admin,
27+
audit deny capability sys_module,
28+
audit deny capability sys_boot,
29+
audit deny capability sys_ptrace,
30+
31+
# ---- network ----
32+
# FastAPI listens on inet/inet6 (default 127.0.0.1:8443) and talks to
33+
# arbor-daemon over a Unix socket.
34+
network inet stream,
35+
network inet6 stream,
36+
network unix,
37+
audit deny network netlink,
38+
audit deny network raw,
39+
40+
# ---- own state ----
41+
/usr/bin/arbor mr,
42+
/usr/bin/setpriv ix,
43+
/usr/lib/arbor/** mr,
44+
/etc/arbor/ r,
45+
/etc/arbor/arbor.env r,
46+
/etc/arbor/ipc.key r,
47+
/etc/arbor/cert.pem r,
48+
/etc/arbor/key.pem r,
49+
/var/lib/arbor/ rw,
50+
/var/lib/arbor/auth.db rwk,
51+
/var/lib/arbor/auth.db-journal rwk,
52+
/var/log/arbor/ rw,
53+
/var/log/arbor/web.log rw,
54+
/run/arbor/ r,
55+
/run/arbor/daemon.sock rw,
56+
57+
# ---- TOTP secret read (when login TOTP is enabled) ----
58+
# Required if ARBOR_TOTP_SECRET_FILE points here. Mode 0600 root:arbor
59+
# in production; the file ACL is the primary gate, this just lets the
60+
# arbor user open it via the profile.
61+
/etc/arbor/totp.secret r,
62+
63+
# ---- read static frontend ----
64+
/usr/lib/arbor/frontend/** r,
65+
/usr/lib/arbor/.venv/** r,
66+
67+
# ---- system bits ----
68+
/proc/ r,
69+
/proc/sys/kernel/random/uuid r,
70+
/dev/null rw,
71+
/dev/urandom r,
72+
/tmp/ r,
73+
74+
# ---- explicit deny ----
75+
# The web process must never touch Portage state — that's the daemon's
76+
# job. Closing this path means a RCE in FastAPI cannot bypass the IPC
77+
# and write directly to /etc/portage.
78+
audit deny /etc/portage/** rwklx,
79+
audit deny /var/db/pkg/** rwklx,
80+
audit deny /var/cache/distfiles/** rwklx,
81+
audit deny /var/tmp/portage/** rwklx,
82+
audit deny /var/lib/portage/** rwklx,
83+
84+
audit deny /home/** rwklx,
85+
audit deny /root/** rwklx,
86+
audit deny /etc/shadow* rwklx,
87+
}

apparmor/usr.bin.arbor-daemon

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# AppArmor profile for /usr/bin/arbor-daemon
2+
#
3+
# *** UNTESTED ***
4+
#
5+
# This profile is provided as a starting point for hardening Arbor's
6+
# privilege daemon under AppArmor on Gentoo (USE flag apparmor on
7+
# sys-apps/apparmor and a kernel built with CONFIG_SECURITY_APPARMOR).
8+
# It has *not* been validated against a full emerge workflow yet.
9+
# Treat it as a draft and iterate against real installs on a test box.
10+
#
11+
# To activate (manual, opt-in):
12+
# sudo cp apparmor/usr.bin.arbor-daemon /etc/apparmor.d/
13+
# sudo apparmor_parser -r /etc/apparmor.d/usr.bin.arbor-daemon
14+
# sudo rc-service arbor-daemon restart # or systemctl restart arbor-daemon
15+
#
16+
# To run in complain-mode while iterating:
17+
# sudo aa-complain /etc/apparmor.d/usr.bin.arbor-daemon
18+
# When happy:
19+
# sudo aa-enforce /etc/apparmor.d/usr.bin.arbor-daemon
20+
21+
#include <tunables/global>
22+
23+
profile arbor-daemon /usr/bin/arbor-daemon {
24+
#include <abstractions/base>
25+
#include <abstractions/python>
26+
#include <abstractions/openssl>
27+
28+
# ---- capabilities ----
29+
capability chown,
30+
capability dac_override,
31+
capability dac_read_search,
32+
capability fowner,
33+
capability fsetid,
34+
capability setgid,
35+
capability setuid,
36+
capability sys_chroot,
37+
capability mknod,
38+
capability kill,
39+
capability sys_admin,
40+
capability sys_ptrace,
41+
capability setpcap,
42+
capability sys_resource,
43+
44+
# Explicitly deny what setpriv / CapabilityBoundingSet would also drop.
45+
audit deny capability net_raw,
46+
audit deny capability net_admin,
47+
audit deny capability sys_module,
48+
audit deny capability sys_boot,
49+
audit deny capability sys_time,
50+
audit deny capability syslog,
51+
audit deny capability bpf,
52+
audit deny capability perfmon,
53+
54+
# ---- network ----
55+
# Daemon only listens on /run/arbor/daemon.sock; no inet sockets.
56+
network unix,
57+
audit deny network inet,
58+
audit deny network inet6,
59+
audit deny network netlink,
60+
audit deny network raw,
61+
62+
# ---- arbor own state ----
63+
/usr/bin/arbor-daemon mr,
64+
/usr/bin/setpriv ix,
65+
/usr/lib/arbor/** mr,
66+
/etc/arbor/ r,
67+
/etc/arbor/arbor.env r,
68+
/etc/arbor/ipc.key r,
69+
/etc/arbor/totp.secret r,
70+
/var/lib/arbor/ rw,
71+
/var/lib/arbor/** rwk,
72+
/var/log/arbor/ rw,
73+
/var/log/arbor/** rw,
74+
/run/arbor/ rw,
75+
/run/arbor/** rwk,
76+
77+
# ---- Portage paths ----
78+
/etc/portage/ r,
79+
/etc/portage/** rw,
80+
/var/db/pkg/ rw,
81+
/var/db/pkg/** rwk,
82+
/var/db/repos/ r,
83+
/var/db/repos/** r,
84+
/var/cache/distfiles/ rw,
85+
/var/cache/distfiles/** rwk,
86+
/var/cache/binpkgs/ rw,
87+
/var/cache/binpkgs/** rwk,
88+
/var/cache/edb/ rw,
89+
/var/cache/edb/** rwk,
90+
/var/tmp/portage/ rw,
91+
/var/tmp/portage/** rwklmix,
92+
/var/lib/portage/ rw,
93+
/var/lib/portage/** rwk,
94+
95+
# ---- emerge tools the daemon execs ----
96+
/usr/bin/emerge ix,
97+
/usr/bin/portageq ix,
98+
/usr/bin/eselect ix,
99+
/usr/bin/ebuild ix,
100+
/usr/bin/eclean ix,
101+
/usr/bin/equery ix,
102+
/usr/sbin/etc-update ix,
103+
/usr/sbin/dispatch-conf ix,
104+
/usr/bin/python3* ix,
105+
/usr/bin/find ix,
106+
/bin/sh ix,
107+
/bin/bash ix,
108+
109+
# ---- system bits emerge needs ----
110+
/proc/ r,
111+
/proc/sys/kernel/random/uuid r,
112+
/proc/[0-9]*/ r,
113+
/proc/[0-9]*/status r,
114+
/proc/[0-9]*/cmdline r,
115+
/proc/[0-9]*/stat r,
116+
/sys/devices/system/cpu/ r,
117+
/sys/devices/system/cpu/** r,
118+
/dev/null rw,
119+
/dev/zero rw,
120+
/dev/urandom r,
121+
/dev/random r,
122+
/dev/tty rw,
123+
/tmp/ r,
124+
/tmp/** rw,
125+
126+
# ---- explicit deny on sensitive paths ----
127+
audit deny /home/** rwklx,
128+
audit deny /root/** rwklx,
129+
audit deny /etc/shadow* rwklx,
130+
audit deny /etc/sudoers* rwklx,
131+
audit deny /etc/ssh/ssh_host_*_key rwklx,
132+
audit deny /proc/sys/kernel/core_pattern w,
133+
audit deny /proc/sys/kernel/modprobe w,
134+
audit deny /sys/kernel/security/** w,
135+
}

0 commit comments

Comments
 (0)