Repository navigation
Merge upstream quattro (PHP via mise, omarchy up, leftover factory/hook guards) #32
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
Closed
Closed
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
e18b5aa
Refuse hook and state names that are paths
Adolanium 9a54c41
Erase old password hashes during factory reset
AksharP5 a6f02d1
Remove the subuid and subgid backups during the factory scrub
omarchybot c84ebda
Refuse path-like names in omarchy-hook-install too
Adolanium a4ec934
Loosen the offline Node pin so mise up tracks new releases
dhh 1ce5592
feat: improves php and laravel installation
nunomaduro d97b2b6
chore: removes useless comment
nunomaduro fcf9e66
fix: removes the laravel binary even when php is already gone
nunomaduro fbbbb32
chore: removes comment
nunomaduro 75e8fd1
Detach 1Password from installer terminal
dhh f049e1f
Add omarchy up alias
dhh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| #!/bin/bash | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" | ||
|
|
||
| if (( EUID != 0 )); then | ||
| if unshare --user --map-root-user true 2>/dev/null; then | ||
| exec unshare --user --map-root-user bash "$0" | ||
| fi | ||
| pass "no unprivileged user namespace; skipping factory account cleanup" | ||
| exit 0 | ||
| fi | ||
|
|
||
| test_tmp=$(mktemp -d) | ||
| trap 'rm -rf "$test_tmp"' EXIT | ||
|
|
||
| # Load the production functions without self-elevation or the reset entrypoint. | ||
| awk ' | ||
| /^[a-z_]+\(\) \{/ { copying = 1 } | ||
| copying { print } | ||
| /^}/ { copying = 0 } | ||
| ' "$ROOT/bin/omarchy-system-factory-reset" >"$test_tmp/functions" | ||
|
|
||
| cat >"$test_tmp/reset" <<'SH' | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
| source "$1/functions" | ||
| TOP_MNT="$2" | ||
| NEXT_NAME=@omarchy-reset-next | ||
| PROVISIONING_DIR=/var/lib/omarchy/provisioning | ||
| LOG_FILE="$TOP_MNT/reset.log" | ||
|
|
||
| log() { printf '%s\n' "$1" >>"$LOG_FILE"; } | ||
| fail() { log "$1"; exit 1; } | ||
|
|
||
| # Account tools are real. Only snapshots, boot rebuilding, and system services | ||
| # are replaced: all writes stay inside this test's disposable directory. | ||
| btrfs() { | ||
| if [[ $1 == "subvolume" && $2 == "snapshot" ]]; then | ||
| mkdir -p "$4" | ||
| cp -a "$3/." "$4/" | ||
| elif [[ $1 == "property" ]]; then | ||
| printf '%s\n' "$6" >"$4/read-only" | ||
| else | ||
| return 1 | ||
| fi | ||
| } | ||
| systemd-id128() { printf '%032d\n' 1; } | ||
| install_provisioning_units() { :; } | ||
| encrypted_install() { return 1; } | ||
| rebuild_next_boot() { touch "$TOP_MNT/rebuilt"; } | ||
| sync() { :; } | ||
|
|
||
| userdel() { | ||
| [[ ${FAIL_COMMAND:-} == "userdel" && $2 == "$FAIL_ROOT" ]] && return 42 | ||
| command userdel "$@" | ||
| } | ||
| usermod() { | ||
| [[ ${FAIL_COMMAND:-} == "usermod" && $2 == "$FAIL_ROOT" ]] && return 42 | ||
| command usermod "$@" | ||
| } | ||
| rm() { | ||
| [[ ${FAIL_COMMAND:-} == "rm" && $* == *"$FAIL_ROOT/etc/shadow-"* ]] && return 42 | ||
| command rm "$@" | ||
| } | ||
|
|
||
| stage_full_reset | ||
| SH | ||
|
|
||
| make_fixture() { | ||
| local top="$1" root_hash="${2:-original-root-hash}" | ||
| local factory="$top/@factory" | ||
| mkdir -p "$top/@" "$factory/etc" "$factory/home/seller" \ | ||
| "$factory/usr/bin" "$factory/usr/share/omarchy/install/provisioning" \ | ||
| "$factory/var/lib/omarchy/provisioning/packages" | ||
| touch "$top/@/old-system" "$factory/home/seller/private-file" \ | ||
| "$factory/usr/share/omarchy/install/provisioning/omarchy-provision-owner.service" \ | ||
| "$factory/var/lib/omarchy/provisioning/packages/node-v0.tar.gz" | ||
| printf '#!/bin/bash\n' >"$factory/usr/bin/omarchy-provision-owner" | ||
| chmod +x "$factory/usr/bin/omarchy-provision-owner" | ||
| printf 'true\n' >"$factory/read-only" | ||
| cat >"$factory/etc/passwd" <<'EOF' | ||
| root:x:0:0:root:/root:/bin/bash | ||
| daemon:x:1:1:daemon:/:/usr/bin/nologin | ||
| seller:x:1000:1000:Seller:/home/seller:/bin/bash | ||
| EOF | ||
| printf 'root:%s:20000:0:99999:7:::\ndaemon:*:20000:0:99999:7:::\nseller:original-user-hash:20000:0:99999:7:::\n' \ | ||
| "$root_hash" >"$factory/etc/shadow" | ||
| printf 'root:x:0:\ndaemon:x:1:\nseller:x:1000:\nwheel:x:998:seller\n' >"$factory/etc/group" | ||
| printf 'root:!::\ndaemon:!::\nseller:!::\nwheel:!::seller\n' >"$factory/etc/gshadow" | ||
| printf 'USERGROUPS_ENAB yes\n' >"$factory/etc/login.defs" | ||
| printf 'seller:100000:65536\n' >"$factory/etc/subuid" | ||
| printf 'seller:100000:65536\n' >"$factory/etc/subgid" | ||
| chmod 600 "$factory/etc/"{shadow,gshadow} | ||
| for file in passwd shadow group gshadow subuid subgid; do | ||
| cp "$factory/etc/$file" "$factory/etc/$file-" | ||
| done | ||
| } | ||
|
|
||
| assert_scrubbed() { | ||
| local root="$1" file | ||
| [[ $(awk -F: '$1 == "root" { print $2 }' "$root/etc/shadow") == "!" ]] || | ||
| fail "reset erases the root hash while keeping the account locked" | ||
| ! grep -q 'original-.*-hash\|seller' "$root/etc/"{passwd,shadow,group,gshadow} || | ||
| fail "reset removes seller account credentials and group membership" | ||
| [[ ! -e $root/home/seller ]] || fail "reset removes the seller's baseline home" | ||
| grep -q '^daemon:\*:' "$root/etc/shadow" || fail "reset preserves service accounts" | ||
| [[ $(stat -c '%a' "$root/etc/shadow") == "600" ]] || fail "shadow stays private" | ||
| for file in passwd shadow group gshadow subuid subgid; do | ||
| [[ ! -e $root/etc/$file- ]] || fail "reset removes the $file backup" | ||
| done | ||
| } | ||
|
|
||
| for scenario in normal locked; do | ||
| top="$test_tmp/$scenario" | ||
| if [[ $scenario == "locked" ]]; then | ||
| make_fixture "$top" '!' | ||
| else | ||
| make_fixture "$top" | ||
| fi | ||
| bash "$test_tmp/reset" "$test_tmp" "$top" || fail "$scenario reset stages successfully" | ||
| assert_scrubbed "$top/@factory" | ||
| assert_scrubbed "$top/@" | ||
| [[ $(cat "$top/@factory/read-only") == "true" ]] || fail "baseline returns to read-only" | ||
| [[ -f $top/@/var/lib/omarchy/provisioning/pending && -f $top/rebuilt ]] || | ||
| fail "reset reaches provisioning after cleanup" | ||
|
|
||
| bash "$test_tmp/reset" "$test_tmp" "$top" || fail "$scenario reset can be repeated" | ||
| assert_scrubbed "$top/@factory" | ||
| assert_scrubbed "$top/@" | ||
| pass "$scenario reset scrubs both roots, preserves service accounts, and can be repeated" | ||
| done | ||
|
|
||
| for target in @omarchy-reset-next @factory; do | ||
| for command in userdel usermod rm; do | ||
| top="$test_tmp/fail-$target-$command" | ||
| make_fixture "$top" | ||
| if FAIL_COMMAND="$command" FAIL_ROOT="$top/$target" bash "$test_tmp/reset" "$test_tmp" "$top"; then | ||
| fail "reset accepted failed $command in $target" | ||
| fi | ||
| [[ -f $top/@/old-system && ! -e $top/rebuilt ]] || | ||
| fail "failed cleanup must not activate or rebuild the reset system" | ||
| [[ $(cat "$top/@factory/read-only") == "true" ]] || | ||
| fail "failed cleanup must leave the baseline read-only" | ||
| pass "failed $command in $target aborts reset before activation" | ||
| done | ||
| done |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
🔒 Agentic Security Review
Severity: HIGH
scrub_factory_accountsinterpolates uid≥1000 login names from the untrusted factory snapshot/etc/passwdinto a hostrm -rf "${root}/home/$user"afteruserdel --root. Factory reset runs as root because the previous owner (and@factory) are untrusted. A planted login such as..or../../../../../etcis accepted byuserdel --rootwhen present in that passwd file, and the followingrmis not confined to the snapshot, so it can wipe the snapshot root or delete live host paths during reset.Impact: A previous owner can sabotage factory reset or delete arbitrary host paths as root when the new owner runs reset.
Reviewed by Cursor Security Reviewer for commit f049e1f. Configure here.