Skip to content

opsec - #1535

Merged
shunkakinoki merged 4 commits into
mainfrom
opsec
Apr 21, 2026
Merged

opsec#1535
shunkakinoki merged 4 commits into
mainfrom
opsec

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented Apr 21, 2026

Copy link
Copy Markdown
Owner
  • feat: opsec
  • feat: opsec

Summary by cubic

Adds a hook to block gh commands that modify repo settings and hardens the matic host’s network and audit config. This prevents accidental repo mutations and improves system visibility.

  • New Features
    • Added block-gh-settings.sh and wired it into config/claude/settings.json: blocks gh repo delete|rename|archive|transfer|edit and gh api -X PATCH|DELETE|PUT /repos/...; includes ShellSpec tests and is listed in spec/coverage_spec.sh. Renamed block-push-main.sh to block-git-push.sh and updated references in config/claude/settings.json, config/codex/hooks.json, and tests.
    • Hardened named-hosts/matic/default.nix: enabled firewall with trusted tailscale0 and refused-connection logs, Wi‑Fi scan MAC randomization and stable-SSID MAC, and auditd rules for exec, priv‑esc, identity files, and SSH.

Written for commit 06f2ee2. Summary will update on new commits.

@mesa-dot-dev

mesa-dot-dev Bot commented Apr 21, 2026

Copy link
Copy Markdown

You do not have enough credits to review this pull request. Please purchase more credits to continue.

@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added GitHub CLI command protection that prevents execution of destructive repository operations such as delete, rename, archive, and transfer.
    • Enabled system firewall with trusted interface configuration.
    • Implemented Linux audit logging for enhanced security monitoring of system calls and privilege escalation events.
    • Enhanced Wi-Fi security with randomized MAC address scanning.
  • Tests

    • Added test coverage for the new GitHub CLI command protection feature.

Walkthrough

The PR adds a new GitHub-CLI-blocking hook and test coverage, swaps and renames an existing git-push hook, updates Claude and Codex PreToolUse hook configurations to run the new scripts, and enables firewall/auditing changes on the Matic host.

Changes

Cohort / File(s) Summary
Claude & Codex Hook Configs
config/claude/settings.json, config/codex/hooks.json
Replaced block-push-main.sh with block-git-push.sh and inserted a new block-gh-settings.sh PreToolUse command for Bash with 5s timeouts.
New GitHub CLI blocker
config/shared/hooks/block-gh-settings.sh
Added script that reads JSON stdin to extract the intended gh command and blocks destructive repo/settings operations (e.g., `gh repo delete
Existing git-push hook rename
config/shared/hooks/block-git-push.sh
Renamed header/message from block-push-main.shblock-git-push.sh; updated emitted BLOCKED message text; no logic changes.
Tests & coverage
spec/block_gh_settings_spec.sh, spec/block_git_push_spec.sh, spec/coverage_spec.sh
Added comprehensive tests for block-gh-settings.sh; updated tests/labels for block-git-push.sh; updated coverage allowlist to include the new scripts and remove the old name.
Host security config
named-hosts/matic/default.nix
Enabled Wi‑Fi power save settings change, randomized MAC scanning, set stable MAC, enabled firewall (trusted tailscale0, refused logging), and enabled auditd with execve, privilege escalation, and sensitive-file watches.

Sequence Diagram(s)

sequenceDiagram
  participant Claude
  participant HookRunner as PreToolUse Hooks
  participant BlockGH as block-gh-settings.sh
  participant BlockPush as block-git-push.sh
  participant Tool as gh (GitHub CLI)

  Claude->>HookRunner: invoke PreToolUse (Bash)
  HookRunner->>BlockPush: run block-git-push.sh
  BlockPush-->>HookRunner: exit 0|2
  HookRunner->>BlockGH: run block-gh-settings.sh
  BlockGH-->>HookRunner: exit 0 (allow) or 2 (BLOCKED)
  HookRunner->>Tool: proceed only if hooks exit 0
  Tool-->>Claude: command executed or blocked
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

Poem

🐰 A little rabbit guards the tree,
Hops in scripts to keep things tidy,
Blocks the naughty gh commands with glee,
Watches logs and audits nightly,
Hooray — the warren stays quite safe and tidy!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'opsec' is vague and generic, providing no meaningful information about the specific changes made in the changeset. Replace with a more descriptive title that summarizes the primary changes, such as 'Add gh-settings hook and harden matic host security configuration'.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description is well-structured and clearly explains the main changes: adding a gh-settings blocking hook, renaming block-push-main.sh to block-git-push.sh, and hardening the matic host configuration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch opsec

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mesa-dot-dev

mesa-dot-dev Bot commented Apr 21, 2026

Copy link
Copy Markdown

Mesa Description

TL;DR

Adds a hook to block gh commands that modify repository settings, and hardens the matic host's network and audit configuration to prevent accidental repo mutations and improve system visibility.

What changed?

  • config/claude/settings.json: Modified a hook command path and added a new command block for block-gh-settings.sh.
  • config/shared/hooks/block-gh-settings.sh: New script to prevent gh commands (e.g., repo delete, api -X PATCH) that modify repository settings.
  • named-hosts/matic/default.nix: Enhanced NetworkManager WiFi privacy, introduced a new firewall configuration with tailscale0 trusted and refused-connection logging, and enabled audit logging for exec, priv-esc, identity files, and SSH.
  • spec/block_gh_settings_spec.sh: New shell integration tests to validate the block-gh-settings.sh script.
  • config/codex/hooks.json: Updated reference from block-push-main.sh to block-git-push.sh.
  • config/shared/hooks/block-git-push.sh: Renamed block-push-main.sh to block-git-push.sh and updated internal references.
  • spec/block_git_push_spec.sh: Renamed spec/block_push_main_spec.sh to spec/block_git_push_spec.sh and updated tests for the renamed script.
  • spec/coverage_spec.sh: Updated test specification to reflect the rename of the Git push blocking hook script.

Description generated by Mesa. Update settings

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a security hook to block destructive GitHub CLI commands and API mutations, accompanied by a new test suite. Additionally, it updates the NixOS configuration for the 'matic' host to include WiFi MAC randomization, firewall settings, and audit logging. Review feedback suggested hardening the command-blocking regex to prevent bypasses and simplifying subcommand extraction in the shell script.

Comment on lines +24 to +25
if echo "$command" | grep -qE '\-X\s+(PATCH|DELETE|PUT)' && echo "$command" | grep -qE '/repos/'; then
method=$(echo "$command" | grep -oE '\-X\s+(PATCH|DELETE|PUT)' | awk '{print $2}')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The gh api block logic is susceptible to bypasses. It currently only checks for the -X flag with a mandatory space, missing --method, --method=, and -X without a space (e.g., -XPATCH). The regex should be updated to handle these variations to ensure the security hook is effective.

Suggested change
if echo "$command" | grep -qE '\-X\s+(PATCH|DELETE|PUT)' && echo "$command" | grep -qE '/repos/'; then
method=$(echo "$command" | grep -oE '\-X\s+(PATCH|DELETE|PUT)' | awk '{print $2}')
if echo "$command" | grep -qE '(\s-X\s*|\s--method[=\s]+)(PATCH|DELETE|PUT)\b' && echo "$command" | grep -qE '/repos/'; then
method=$(echo "$command" | grep -oE '\b(PATCH|DELETE|PUT)\b' | head -n1)


# Block: gh repo <destructive-subcommand>
if echo "$command" | grep -qE 'gh\s+repo\s+(delete|rename|archive|transfer|edit)\b'; then
subcommand=$(echo "$command" | grep -oE 'gh\s+repo\s+(delete|rename|archive|transfer|edit)' | awk '{print $3}')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The extraction of the subcommand can be simplified. Using head -n1 ensures that only the first match is captured if multiple keywords appear in the string (e.g., in comments), and it avoids the overhead of spawning awk.

Suggested change
subcommand=$(echo "$command" | grep -oE 'gh\s+repo\s+(delete|rename|archive|transfer|edit)' | awk '{print $3}')
subcommand=$(echo "$command" | grep -oE '\b(delete|rename|archive|transfer|edit)\b' | head -n1)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
config/shared/hooks/block-gh-settings.sh (1)

11-12: Minor: jq stderr is swallowed; empty command key passes through as allowed.

2>/dev/null on jq hides malformed-JSON failures (the script then treats command as empty and exits 0, i.e., allows the tool call). That is consistent with the {} / empty-string tests, but note it also means a malformed payload from a future caller silently bypasses the guard. If that matters, consider exit 0 only when jq succeeds with empty output, and fail-closed otherwise. Non-blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@config/shared/hooks/block-gh-settings.sh` around lines 11 - 12, The current
extraction command uses `command=$(echo "$input" | jq -r '.tool_input.command //
.command // empty' 2>/dev/null)` which swallows jq errors and treats malformed
JSON as an empty command (allowing the call); change this so you run jq without
redirecting stderr, capture its exit status, and only call `exit 0` when jq
succeeded and produced an empty string—if jq fails (non-zero exit) return a
non-zero exit (fail-closed). Concretely: run the same jq expression on `$input`,
check `$?` (or capture jq output+status), and if status != 0 then exit 1 (or
log+exit 1); if status == 0 and the extracted `command` is empty then exit 0;
otherwise proceed. This affects the `command` assignment and the `[[ -z $command
]] && exit 0` logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@config/shared/hooks/block-gh-settings.sh`:
- Around line 22-30: The current gh api mutation detection only matches "-X" and
a leading "/repos/", so update the two grep checks and the method extraction to
also handle the long form "--method" (with either space or "=" delimiter) and
repo paths without a leading slash. Specifically, replace the grep that detects
'-X' with one that matches '(-X|--method)(=|[[:space:]]+)(PATCH|DELETE|PUT)' and
replace the '/repos/' check with a pattern that allows an optional leading slash
like '(/?repos/)'; update the method assignment (the method= line) to extract
the HTTP verb from the combined '-X|--method' match (e.g., capture the
PATCH|DELETE|PUT token from the matched string) so the msg and detection remain
correct.

In `@named-hosts/matic/default.nix`:
- Around line 92-100: The priv_esc audit rule in the rules array only applies to
"arch=b64" so 32-bit setuid/setgid syscalls can bypass it; add a matching "-a
exit,always -F arch=b32 -S setuid,setgid,setresuid,setresgid -k priv_esc" entry
alongside the existing b64 priv_esc rule (keeping the existing duplicated execve
b64/b32 entries unchanged), and while editing consider verifying or configuring
auditd log rotation/size to avoid excessive AUDIT_EXECVE noise.
- Around line 80-86: The firewall config references a non-existent interface and
may enforce unintended default-deny behavior; either remove the dead rule or
enable Tailscale: either delete or comment out the "trustedInterfaces = [
\"tailscale0\" ]" entry in the networking.firewall block (and consider removing
empty allowedTCPPorts/allowedUDPPorts or adding needed service ports) OR enable
Tailscale by adding modules.tailscale with installSystemService = true (so the
tailscale0 interface is created), and keep logRefusedConnections true only if
you want denials logged. Ensure you update the networking.firewall block and/or
add modules.tailscale so the configuration is consistent.

---

Nitpick comments:
In `@config/shared/hooks/block-gh-settings.sh`:
- Around line 11-12: The current extraction command uses `command=$(echo
"$input" | jq -r '.tool_input.command // .command // empty' 2>/dev/null)` which
swallows jq errors and treats malformed JSON as an empty command (allowing the
call); change this so you run jq without redirecting stderr, capture its exit
status, and only call `exit 0` when jq succeeded and produced an empty string—if
jq fails (non-zero exit) return a non-zero exit (fail-closed). Concretely: run
the same jq expression on `$input`, check `$?` (or capture jq output+status),
and if status != 0 then exit 1 (or log+exit 1); if status == 0 and the extracted
`command` is empty then exit 0; otherwise proceed. This affects the `command`
assignment and the `[[ -z $command ]] && exit 0` logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 797c9f5f-527b-49e5-bab2-7ae38fc84028

📥 Commits

Reviewing files that changed from the base of the PR and between 319982f and 2dd465e.

📒 Files selected for processing (4)
  • config/claude/settings.json
  • config/shared/hooks/block-gh-settings.sh
  • named-hosts/matic/default.nix
  • spec/block_gh_settings_spec.sh

Comment on lines +22 to +30
# Block: gh api -X PATCH|DELETE|PUT targeting /repos/
if echo "$command" | grep -qE 'gh\s+api'; then
if echo "$command" | grep -qE '\-X\s+(PATCH|DELETE|PUT)' && echo "$command" | grep -qE '/repos/'; then
method=$(echo "$command" | grep -oE '\-X\s+(PATCH|DELETE|PUT)' | awk '{print $2}')
msg="'gh api -X $method /repos/...' is blocked. Repo API mutations must be done manually."
echo "BLOCKED by block-gh-settings.sh: $msg" >&2
exit 2
fi
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does gh CLI gh apiaccept both--method DELETEand paths without leading slash likerepos/owner/repo?

💡 Result:

Yes, the gh CLI gh api command accepts both --method DELETE (or the short form -X DELETE) and paths without a leading slash like repos/owner/repo. The official documentation shows examples using -X for methods like GET and PATCH, confirming --method DELETE works similarly as it overrides the default method. A GitHub CLI maintainer example explicitly uses gh api -X DELETE repos/${username}/${reponame} for deleting a repository, demonstrating DELETE with a path starting repos/owner/repo without a leading slash. Multiple sources confirm gh api accepts paths both with and without a leading slash (e.g., /repos/... or repos/...), prepending the base API URL appropriately. Examples in the manual consistently use paths without leading slashes like repos/{owner}/{repo}/releases. While Git Bash on Windows may misinterpret leading slashes due to path expansion, this is a shell issue, not a limitation of gh CLI, and paths without leading slashes work universally.

Citations:


Tighten regex patterns to close bypass vectors in gh api mutation detection.

The script blocks destructive mutations via -X DELETE /repos/..., but two real bypasses exist:

  1. --method DELETE flag works equivalently to -X DELETE per official gh CLI documentation, but the regex only matches -X
  2. Paths work without leading slash (repos/owner/repo not just /repos/owner/repo) per documentation and GitHub maintainer examples

Both bypasses should be closed. Update the regex patterns to match:

  • -X and --method forms (with spacing/delimiter variations: -X DELETE, --method=DELETE, --method DELETE)
  • /repos/ and repos/ path formats (with optional leading slash)
Proposed fix
 # Block: gh api -X PATCH|DELETE|PUT targeting /repos/
 if echo "$command" | grep -qE 'gh\s+api'; then
-  if echo "$command" | grep -qE '\-X\s+(PATCH|DELETE|PUT)' && echo "$command" | grep -qE '/repos/'; then
-    method=$(echo "$command" | grep -oE '\-X\s+(PATCH|DELETE|PUT)' | awk '{print $2}')
+  if echo "$command" | grep -qE '(-X|--method)[= ]+(PATCH|DELETE|PUT)\b' \
+     && echo "$command" | grep -qE '(^|[[:space:]=])/?repos/'; then
+    method=$(echo "$command" | grep -oE '(-X|--method)[= ]+(PATCH|DELETE|PUT)' | grep -oE '(PATCH|DELETE|PUT)')
     msg="'gh api -X $method /repos/...' is blocked. Repo API mutations must be done manually."
     echo "BLOCKED by block-gh-settings.sh: $msg" >&2
     exit 2
   fi
 fi

Add test cases in spec/block_gh_settings_spec.sh:

It 'blocks gh api --method DELETE /repos/...'
Data '{"tool_input": {"command": "gh api --method DELETE /repos/owner/repo"}}'
When run bash "$SCRIPT"
The status should eq 2
The stderr should include 'BLOCKED'
End

It 'blocks gh api -X DELETE repos/... (no leading slash)'
Data '{"tool_input": {"command": "gh api -X DELETE repos/owner/repo"}}'
When run bash "$SCRIPT"
The status should eq 2
The stderr should include 'BLOCKED'
End
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@config/shared/hooks/block-gh-settings.sh` around lines 22 - 30, The current
gh api mutation detection only matches "-X" and a leading "/repos/", so update
the two grep checks and the method extraction to also handle the long form
"--method" (with either space or "=" delimiter) and repo paths without a leading
slash. Specifically, replace the grep that detects '-X' with one that matches
'(-X|--method)(=|[[:space:]]+)(PATCH|DELETE|PUT)' and replace the '/repos/'
check with a pattern that allows an optional leading slash like '(/?repos/)';
update the method assignment (the method= line) to extract the HTTP verb from
the combined '-X|--method' match (e.g., capture the PATCH|DELETE|PUT token from
the matched string) so the msg and detection remain correct.

Comment on lines +80 to +86
networking.firewall = {
enable = true;
trustedInterfaces = [ "tailscale0" ];
allowedTCPPorts = [ ];
allowedUDPPorts = [ ];
logRefusedConnections = true;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether tailscale is actually enabled on the matic host (directly or transitively).
rg -nP -C2 '\b(services\.tailscale|modules\.tailscale)\b' named-hosts/matic/ hosts/ modules/ 2>/dev/null
echo '---'
# Also check if any imported module pulls in tailscale for matic.
fd -t f . named-hosts/matic hosts | xargs rg -nP '\btailscale\b' 2>/dev/null

Repository: shunkakinoki/dotfiles

Length of output: 70


Remove dead firewall config or enable Tailscale on this host.

trustedInterfaces = [ "tailscale0" ] references an interface that isn't enabled anywhere in matic's configuration (unlike named-hosts/kyber/default.nix, which enables modules.tailscale with installSystemService = true). If tailscale0 never comes up, this is dead firewall config.

Combined with allowedTCPPorts = [ ] and allowedUDPPorts = [ ], the default-deny stance may also block local service discovery you rely on (mDNS/Avahi for printers, CUPS, KDEConnect/GSConnect, Steam in-home streaming, Spotify Connect, etc.). The logRefusedConnections = true setting will surface these denials in the journal, but that's reactive rather than preventive. Either enable Tailscale on this host or remove the tailscale0 trust rule.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@named-hosts/matic/default.nix` around lines 80 - 86, The firewall config
references a non-existent interface and may enforce unintended default-deny
behavior; either remove the dead rule or enable Tailscale: either delete or
comment out the "trustedInterfaces = [ \"tailscale0\" ]" entry in the
networking.firewall block (and consider removing empty
allowedTCPPorts/allowedUDPPorts or adding needed service ports) OR enable
Tailscale by adding modules.tailscale with installSystemService = true (so the
tailscale0 interface is created), and keep logRefusedConnections true only if
you want denials logged. Ensure you update the networking.firewall block and/or
add modules.tailscale so the configuration is consistent.

Comment on lines +92 to +100
rules = [
"-a exit,always -F arch=b64 -S execve -k exec"
"-a exit,always -F arch=b32 -S execve -k exec"
"-a exit,always -F arch=b64 -S setuid,setgid,setresuid,setresgid -k priv_esc"
"-w /etc/sudoers -p wa -k sudoers"
"-w /etc/passwd -p wa -k identity"
"-w /etc/shadow -p wa -k identity"
"-w /etc/ssh -p wa -k ssh"
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

setuid/setgid audit rule only covers arch=b64 — 32-bit binaries bypass it.

The execve rule is correctly duplicated for both b64 and b32, but the privilege-escalation rule is only installed for b64. On x86_64, any 32-bit setuid/setgid syscall (e.g. invoked by a multilib binary from Steam, Wine, or a compiled-in-32-bit tool) will silently evade the priv_esc key. Add the matching b32 rule for parity with the execve coverage.

Also consider whether unconditional execve auditing is desired here: on a desktop running Hyprland + Steam + Docker this will emit a very high volume of AUDIT_EXECVE records and, without auditd log rotation tuned, can fill /var/log/audit quickly. At minimum verify security.auditd defaults or add an explicit rotation/size cap.

🔒 Proposed fix: add b32 priv-esc rule
           rules = [
             "-a exit,always -F arch=b64 -S execve -k exec"
             "-a exit,always -F arch=b32 -S execve -k exec"
             "-a exit,always -F arch=b64 -S setuid,setgid,setresuid,setresgid -k priv_esc"
+            "-a exit,always -F arch=b32 -S setuid,setgid,setresuid,setresgid -k priv_esc"
             "-w /etc/sudoers -p wa -k sudoers"
             "-w /etc/passwd -p wa -k identity"
             "-w /etc/shadow -p wa -k identity"
             "-w /etc/ssh -p wa -k ssh"
           ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@named-hosts/matic/default.nix` around lines 92 - 100, The priv_esc audit rule
in the rules array only applies to "arch=b64" so 32-bit setuid/setgid syscalls
can bypass it; add a matching "-a exit,always -F arch=b32 -S
setuid,setgid,setresuid,setresgid -k priv_esc" entry alongside the existing b64
priv_esc rule (keeping the existing duplicated execve b64/b32 entries
unchanged), and while editing consider verifying or configuring auditd log
rotation/size to avoid excessive AUDIT_EXECVE noise.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="named-hosts/matic/default.nix">

<violation number="1" location="named-hosts/matic/default.nix:95">
P2: Missing `b32` counterpart for the privilege escalation audit rule. The `execve` rules above correctly cover both `arch=b64` and `arch=b32`, but this `setuid`/`setgid` rule only monitors 64-bit syscalls. Any 32-bit setuid/setgid binary (e.g., from multilib, Wine, or Steam) will silently evade this audit rule. Add a matching `b32` rule for completeness.</violation>
</file>

<file name="config/shared/hooks/block-gh-settings.sh">

<violation number="1" location="config/shared/hooks/block-gh-settings.sh:24">
P1: The mutation filter is bypassable because it only detects `-X ...` and `/repos/` with a leading slash. `gh api --method PATCH repos/...` will not be blocked.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


# Block: gh api -X PATCH|DELETE|PUT targeting /repos/
if echo "$command" | grep -qE 'gh\s+api'; then
if echo "$command" | grep -qE '\-X\s+(PATCH|DELETE|PUT)' && echo "$command" | grep -qE '/repos/'; then

@cubic-dev-ai cubic-dev-ai Bot Apr 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The mutation filter is bypassable because it only detects -X ... and /repos/ with a leading slash. gh api --method PATCH repos/... will not be blocked.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/shared/hooks/block-gh-settings.sh, line 24:

<comment>The mutation filter is bypassable because it only detects `-X ...` and `/repos/` with a leading slash. `gh api --method PATCH repos/...` will not be blocked.</comment>

<file context>
@@ -0,0 +1,32 @@
+
+# Block: gh api -X PATCH|DELETE|PUT targeting /repos/
+if echo "$command" | grep -qE 'gh\s+api'; then
+  if echo "$command" | grep -qE '\-X\s+(PATCH|DELETE|PUT)' && echo "$command" | grep -qE '/repos/'; then
+    method=$(echo "$command" | grep -oE '\-X\s+(PATCH|DELETE|PUT)' | awk '{print $2}')
+    msg="'gh api -X $method /repos/...' is blocked. Repo API mutations must be done manually."
</file context>
Fix with Cubic

rules = [
"-a exit,always -F arch=b64 -S execve -k exec"
"-a exit,always -F arch=b32 -S execve -k exec"
"-a exit,always -F arch=b64 -S setuid,setgid,setresuid,setresgid -k priv_esc"

@cubic-dev-ai cubic-dev-ai Bot Apr 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Missing b32 counterpart for the privilege escalation audit rule. The execve rules above correctly cover both arch=b64 and arch=b32, but this setuid/setgid rule only monitors 64-bit syscalls. Any 32-bit setuid/setgid binary (e.g., from multilib, Wine, or Steam) will silently evade this audit rule. Add a matching b32 rule for completeness.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At named-hosts/matic/default.nix, line 95:

<comment>Missing `b32` counterpart for the privilege escalation audit rule. The `execve` rules above correctly cover both `arch=b64` and `arch=b32`, but this `setuid`/`setgid` rule only monitors 64-bit syscalls. Any 32-bit setuid/setgid binary (e.g., from multilib, Wine, or Steam) will silently evade this audit rule. Add a matching `b32` rule for completeness.</comment>

<file context>
@@ -71,7 +71,34 @@ import ../../hosts/nixos {
+          rules = [
+            "-a exit,always -F arch=b64 -S execve -k exec"
+            "-a exit,always -F arch=b32 -S execve -k exec"
+            "-a exit,always -F arch=b64 -S setuid,setgid,setresuid,setresgid -k priv_esc"
+            "-w /etc/sudoers -p wa -k sudoers"
+            "-w /etc/passwd -p wa -k identity"
</file context>
Suggested change
"-a exit,always -F arch=b64 -S setuid,setgid,setresuid,setresgid -k priv_esc"
"-a exit,always -F arch=b64 -S setuid,setgid,setresuid,setresgid -k priv_esc"
"-a exit,always -F arch=b32 -S setuid,setgid,setresuid,setresgid -k priv_esc"
Fix with Cubic

@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown

Only repository collaborators, contributors, or members can run CodeRabbit commands.

- Listed the new shared hook so coverage checks expect its matching spec.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
spec/coverage_spec.sh (1)

359-360: LGTM — coverage list correctly updated.

The added block-gh-settings.sh and block-git-push.sh entries align with the hook renames/additions elsewhere in this PR, and the removal of block-push-main.sh keeps the diff-based mismatch check in sync with git ls-files.

Optional nit: for consistency with the upper Describe 'all required scripts have spec files' block (lines 8–305), consider adding explicit It 'has spec file for config/shared/hooks/block-gh-settings.sh' / block-git-push.sh assertions pointing at spec/block_gh_settings_spec.sh and spec/block_git_push_spec.sh. Not required — the bottom diff test already enforces presence in the allowlist — but it keeps the two sections symmetric.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/coverage_spec.sh` around lines 359 - 360, Add two explicit example
assertions to the existing Describe 'all required scripts have spec files' block
to mirror the top section: create It 'has spec file for
config/shared/hooks/block-gh-settings.sh' that checks for
spec/block_gh_settings_spec.sh and It 'has spec file for
config/shared/hooks/block-git-push.sh' that checks for
spec/block_git_push_spec.sh; this keeps the block symmetric with the upper
section while the bottom diff test still enforces the allowlist.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@spec/coverage_spec.sh`:
- Around line 359-360: Add two explicit example assertions to the existing
Describe 'all required scripts have spec files' block to mirror the top section:
create It 'has spec file for config/shared/hooks/block-gh-settings.sh' that
checks for spec/block_gh_settings_spec.sh and It 'has spec file for
config/shared/hooks/block-git-push.sh' that checks for
spec/block_git_push_spec.sh; this keeps the block symmetric with the upper
section while the bottom diff test still enforces the allowlist.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0020f335-a0d4-400b-9bb9-ed1b830d30ec

📥 Commits

Reviewing files that changed from the base of the PR and between 2dd465e and 06f2ee2.

📒 Files selected for processing (5)
  • config/claude/settings.json
  • config/codex/hooks.json
  • config/shared/hooks/block-git-push.sh
  • spec/block_git_push_spec.sh
  • spec/coverage_spec.sh
✅ Files skipped from review due to trivial changes (3)
  • spec/block_git_push_spec.sh
  • config/shared/hooks/block-git-push.sh
  • config/codex/hooks.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • config/claude/settings.json

@shunkakinoki
shunkakinoki merged commit d46fa2d into main Apr 21, 2026
28 checks passed
@shunkakinoki
shunkakinoki deleted the opsec branch April 21, 2026 20:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant