-
Notifications
You must be signed in to change notification settings - Fork 0
Who is writing a commit is a question of its own, and a path is not a token #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| # Base rule set: mismatched-author (a commit stamped with an identity that is | ||
| # not yours) | ||
| # | ||
| # The guard that refuses a commit whose author or committer identity disagrees | ||
| # with the global one on the machine performing it. Pull in with: | ||
| # | ||
| # [inherit] | ||
| # sets = ["process-residue", "mismatched-author"] | ||
| # | ||
| # WHAT IT REFUSES. The identity git is about to record, resolved through `git | ||
| # var GIT_AUTHOR_IDENT` and `git var GIT_COMMITTER_IDENT`, compared against | ||
| # `user.name` and `user.email` in the global config. That resolution is the | ||
| # whole point of the check: it reflects `--author`, a repository-local | ||
| # `user.email`, and any `GIT_AUTHOR_*` or `GIT_COMMITTER_*` in the environment, | ||
| # so every route a stray identity arrives by is read at the one moment it is | ||
| # observable. Afterwards the commit looks exactly like a commit you made. | ||
| # | ||
| # The case it was installed for is an agent or a container that ran `git init`, | ||
| # set whatever local identity was to hand, and committed under it. That commit | ||
| # is not attributed to anybody on the forge, it is not covered by whatever | ||
| # signing the author's real identity carries, and rewriting it later rewrites | ||
| # every commit after it. | ||
| # | ||
| # WHY IT IS ITS OWN SET rather than a rule inside one already here. Three | ||
| # neighbours could have taken it and each would have been a different claim: | ||
| # | ||
| # * `unowned-push` asks where a ref is going and answers from a destination the | ||
| # repository has named. This asks who is writing and answers from the machine. | ||
| # Nothing in this rule reads a remote, and nothing in that one reads a commit. | ||
| # * `host-identity` also reads the running machine -- and then searches | ||
| # committed CONTENT for what it read. It ships `stages = []` and is a scan | ||
| # rule by construction. A guard joining it would push its ceiling off zero, | ||
| # which is the one property that set's header argues for. | ||
| # * `commit-message-residue` is about the text a commit records. An identity is | ||
| # not in that text, it is in the commit's own headers, and the guard reads it | ||
| # before a message exists. | ||
| # | ||
| # Taking a set is a decision about what will be refused and when, which is why | ||
| # every guard set here is named and argued separately. This one has its own | ||
| # cost, below, and it should be possible to decline it while keeping the rest. | ||
| # | ||
| # WHAT INHERITING THIS COSTS. One `pre-commit` hook that runs two `git var` | ||
| # calls, no network and no tree read. It refuses the deliberate `--author` as | ||
| # readily as the accidental one, so a repository that commits on somebody's | ||
| # behalf -- applying a mailed patch, importing a history -- gets a refusal it | ||
| # has to waive with `UPHOLD_ALLOW=prevent-author-mismatch` for that commit. | ||
| # | ||
| # AND WHAT IT CANNOT DO. A machine with no global `user.email` has no | ||
| # expectation to compare against, and the guard says so on stderr and declines | ||
| # rather than refusing. That is the container case verbatim, so the note is not | ||
| # decoration: a guard that quietly does not run in its own scenario reads | ||
| # exactly like a guard that ran and found nothing. There is no field to pin the | ||
| # expected identity on -- the global config IS the expectation -- so a | ||
| # repository that wants the check to bite must have an identity configured for | ||
| # the person sitting at it. | ||
| # | ||
| # PROVENANCE. Promoted after being found declared by hand in 8 policy files | ||
| # across one consuming superproject and its submodules, byte for byte in every | ||
| # one: the same id, the same built-in, the same single `pre-commit` stage, no | ||
| # parameters and no variation anywhere. Eight copies of a three-line | ||
| # declaration is one decision and seven transcriptions, and the transcriptions | ||
| # are what `no-hand-copied-base-rule` reports on. | ||
| # | ||
| # The id is the one those copies already carry, so a repository that still | ||
| # holds its own copy shadows this rule rather than colliding with it, and | ||
| # deleting the copy is a subtraction with nothing to rewrite. | ||
|
|
||
| # WHAT THIS SET MAY INSTALL. `stages` is the ceiling the loader holds the rules | ||
| # to: a rule reaching past it is refused at load, so the set cannot grow a | ||
| # second stage without editing this line, which is a diff a reader sees. One | ||
| # stage, because there is one moment the identity is still a decision. | ||
| [set] | ||
| stages = ["pre-commit"] | ||
|
|
||
| [rule.prevent-author-mismatch] | ||
| builtin = "prevent-author-mismatch" | ||
| git.hooks = ["pre-commit"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,7 +71,16 @@ message = """ | |
| Do not commit hardcoded user home paths. Use neutral paths such as | ||
| /srv/example/... or discover runtime paths from configuration and system state. | ||
| """ | ||
| regexp = '(?:/home|/Users)/[A-Za-z0-9._-]+|[A-Za-z]:\\Users\\[A-Za-z0-9._-]+' | ||
| # THE LEADING BOUNDARY IS NOT DECORATION. Without it the alternation matches | ||
| # `/home` wherever those five characters land inside a longer token, and the | ||
| # tokens are ordinary: `$XDG_DATA_HOME/foo`, a `SOME/home/relative` path in a | ||
| # fixture id, an identifier ending in `/home` that a template joined. Every one | ||
| # of those is a report on a file that holds no home path at all, and a rule | ||
| # whose findings are mostly wrong is the rule that gets excluded wholesale. | ||
| # `(?:^|[^A-Za-z0-9_.-])` says the match starts a path rather than continuing an | ||
| # identifier; the Windows alternative needs no such guard because a drive letter | ||
| # and a colon already are the boundary. | ||
| regexp = '(?:^|[^A-Za-z0-9_.-])(?:/home|/Users)/[A-Za-z0-9._-]+|[A-Za-z]:\\Users\\[A-Za-z0-9._-]+' | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/usr/bin/env bash
python - <<'PY'
import re
pattern = re.compile(
r'(?:^|[^A-Za-z0-9_.-])(?:/home|/Users)/[A-Za-z0-9._-]+'
r'|[A-Za-z]:\\Users\\[A-Za-z0-9._-]+'
)
for value in ("//home/alice", "file:///home/alice", "https://example.test/home/dashboard"):
print(value, bool(pattern.search(value)))
PYRepository: HackingGate/uphold Length of output: 240 🏁 Script executed: #!/usr/bin/env bash
set -eu
printf '%s\n' '--- repository instructions for policy/base and tests ---'
find /tmp/coderabbit-repo-knowledge/hackinggate-uphold-b52bccfc -type f -name '*.md' -print \
| sort
printf '%s\n' '--- changed policy section ---'
cat -n policy/base/process-residue.toml | sed -n '70,92p'
printf '%s\n' '--- directly related corpus references ---'
rg -n -C 3 'process-residue|/home|/Users|https://example\.test|file://|//home' tests policy/base \
--glob '*.rs' --glob '*.toml'Repository: HackingGate/uphold Length of output: 23264 🏁 Script executed: #!/usr/bin/env bash
set -eu
printf '%s\n' '--- applicable repository convention ---'
cat /tmp/coderabbit-repo-knowledge/hackinggate-uphold-b52bccfc/conventions/repo-wide.md
printf '%s\n' '--- regex consumer and corpus harness ---'
rg -n -C 4 'regexp|Regex::new|is_match|base_set_corpus|CORPUS' src tests --glob '*.rs' \
| head -240Repository: HackingGate/uphold Length of output: 18323 🏁 Script executed: #!/usr/bin/env bash
set -eu
printf '%s\n' '--- focused change context ---'
git diff --unified=12 -- policy/base/process-residue.toml tests/base_set_corpus.rs
printf '%s\n' '--- exact search contract ---'
sed -n '1048,1110p' src/shim.rs
rg -n -C 5 'pub.*search_text|fn search_text|struct Query|Query::regex' src/engine.rsRepository: HackingGate/uphold Length of output: 5451 Document protocol-relative and 🤖 Prompt for AI Agents |
||
| files.include = ["."] | ||
| files.exclude = ["**/tests/**", "**/test/**", "**/*_test.go", "policy/**", "README.md"] | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,7 +55,7 @@ | |
| }, | ||
| "id": "no-hardcoded-home-paths", | ||
| "message": "Do not commit hardcoded user home paths. Use neutral paths such as\n/srv/example/... or discover runtime paths from configuration and system state.\n", | ||
| "regexp": "(?:/home|/Users)/[A-Za-z0-9._-]+|[A-Za-z]:\\\\Users\\\\[A-Za-z0-9._-]+" | ||
| "regexp": "(?:^|[^A-Za-z0-9_.-])(?:/home|/Users)/[A-Za-z0-9._-]+|[A-Za-z]:\\\\Users\\\\[A-Za-z0-9._-]+" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Apply the token boundary to the Windows alternative. Line 58 leaves 🤖 Prompt for AI Agents |
||
| }, | ||
| { | ||
| "files": { | ||
|
|
@@ -560,6 +560,24 @@ | |
| "pre-merge-commit" | ||
| ] | ||
| }, | ||
| { | ||
| "commands": [], | ||
| "rules": [ | ||
| { | ||
| "builtin": "prevent-author-mismatch", | ||
| "git": { | ||
| "hooks": [ | ||
| "pre-commit" | ||
| ] | ||
| }, | ||
| "id": "prevent-author-mismatch" | ||
| } | ||
| ], | ||
| "set": "mismatched-author", | ||
| "stages": [ | ||
| "pre-commit" | ||
| ] | ||
| }, | ||
| { | ||
| "commands": [], | ||
| "rules": [ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
Repository: HackingGate/uphold
Length of output: 14128
🏁 Script executed:
Repository: HackingGate/uphold
Length of output: 33067
Authorization Bypass (CWE-863): Incorrect Authorization
Reachability: Internal · Exploitability: Moderate
Fail closed when no global identity exists.
When
user.emailis not configured globally,prevent_author_mismatchreturnsOk(None). Thepre-commitguard then reports a clean result and permits an unverified repository-local or environment-supplied identity. Return an explicit failure and add a regression test for this case.🤖 Prompt for AI Agents
Source: Coding guidelines