tenseleyflow/shithub / 433b238

Browse files

S15: lint-policy-boundary guard + make target

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
433b2385ac353e62c776c36c87f1c53b4b152a41
Parents
e0a88b7
Tree
0eb22ea

2 changed files

StatusFile+-
M Makefile 5 2
A scripts/lint-policy-boundary.sh 71 0
Makefilemodified
@@ -2,7 +2,7 @@
22
 # Targets mirror what CI runs. The Makefile is the source of truth.
33
 
44
 .DEFAULT_GOAL := help
5
-.PHONY: help dev build test test-race lint fmt tidy clean ci assets install-tools version
5
+.PHONY: help dev build test test-race lint lint-policy fmt tidy clean ci assets install-tools version
66
 
77
 # Build metadata embedded into the binary via -ldflags.
88
 VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
@@ -70,9 +70,12 @@ assets: ## Copy Primer CSS into internal/web/static/ for embedding.
7070
 		echo "warn: .refs/primer-css/dist not found; run 'git clone https://github.com/primer/css .refs/primer-css' first"; \
7171
 	fi
7272
 
73
-ci: lint test build ## Full CI pipeline (matches .github/workflows/ci.yml).
73
+ci: lint lint-policy test build ## Full CI pipeline (matches .github/workflows/ci.yml).
7474
 	@echo "ci: ok"
7575
 
76
+lint-policy: ## Enforce policy-package boundary (no inline auth checks in handlers/git/cmd).
77
+	@scripts/lint-policy-boundary.sh
78
+
7679
 install-tools: ## Install development tools via 'go install'.
7780
 	go install mvdan.cc/gofumpt@latest
7881
 	go install golang.org/x/tools/cmd/goimports@latest
scripts/lint-policy-boundary.shadded
@@ -0,0 +1,71 @@
1
+#!/usr/bin/env bash
2
+# SPDX-License-Identifier: AGPL-3.0-or-later
3
+#
4
+# Fail when authorization decisions leak outside internal/auth/policy.
5
+# After S15, every "is this actor allowed to do X?" check must flow
6
+# through policy.Can(); handler/git/cmd code must not branch on
7
+# ownership, visibility, or collaborator state inline.
8
+#
9
+# The check looks for these smells in handler/git/hook code:
10
+#   - `OwnerUserID == ` or `== row.OwnerUserID` style equality compares
11
+#   - `Visibility == reposdb.Repo` equality compares
12
+#   - `IsArchived` used as a control-flow predicate (not a parameter)
13
+#
14
+# Pure data-plumbing references (constructing sqlc.Params, returning
15
+# the column from a query, logging) are allowed — those don't decide
16
+# anything.
17
+#
18
+# Allowed locations (carved out below):
19
+#   internal/auth/policy/...
20
+#   internal/repos/...           (repo-create + repo orchestration)
21
+#   internal/web/handlers/repo/  (constructs the policy actor; lookup
22
+#                                 wrapper is policy-aware)
23
+#   *_test.go everywhere         (tests legitimately seed state)
24
+#
25
+# The script exits 0 when no violations are found, 1 otherwise. Run as
26
+# part of `make ci`.
27
+
28
+set -euo pipefail
29
+
30
+cd "$(git rev-parse --show-toplevel)"
31
+
32
+# Patterns that smell like an inline auth decision.
33
+PATTERNS=(
34
+  '\.OwnerUserID == '
35
+  '\.OwnerUserID\.Int64 == '
36
+  '== .*\.OwnerUserID'
37
+  '\.Visibility == .*RepoVisibility'
38
+  'if .*\.IsArchived '
39
+)
40
+
41
+# Files we're guarding — anywhere a request handler or hook lives.
42
+INCLUDE=(
43
+  ':!internal/auth/policy/*'
44
+  ':!internal/repos/*'
45
+  ':!internal/web/handlers/repo/*'
46
+  ':!**/*_test.go'
47
+)
48
+
49
+violations=0
50
+for pat in "${PATTERNS[@]}"; do
51
+  matches=$(git grep -nE "$pat" -- \
52
+    'internal/web/handlers' 'internal/git' 'cmd/shithubd' \
53
+    "${INCLUDE[@]}" 2>/dev/null || true)
54
+  if [[ -n "$matches" ]]; then
55
+    echo "policy boundary violation — pattern: $pat"
56
+    echo "$matches" | sed 's/^/  /'
57
+    echo
58
+    violations=1
59
+  fi
60
+done
61
+
62
+if [[ "$violations" -ne 0 ]]; then
63
+  echo "------------------------------------------------------------"
64
+  echo "Authorization checks must flow through internal/auth/policy."
65
+  echo "If your case is legitimate data plumbing (e.g. a sqlc.Params"
66
+  echo "field), refactor to read the value from a struct field rather"
67
+  echo "than compare inline."
68
+  echo "------------------------------------------------------------"
69
+  exit 1
70
+fi
71
+echo "policy boundary: clean"