From 6d5f96fef7c4f98613cd2154e5e1ac9a44bbe516 Mon Sep 17 00:00:00 2001 From: bytebeast Date: Thu, 30 Jul 2026 12:40:33 -0700 Subject: [PATCH 1/2] feat(ci): add shell ci and zizmor (AH-2026073051139) Refs: AH-2026073051139 --- .github/workflows/shell-ci.yml | 70 ++++++++++++++++++++++++++++++++++ .github/zizmor.yml | 41 ++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 .github/workflows/shell-ci.yml create mode 100644 .github/zizmor.yml diff --git a/.github/workflows/shell-ci.yml b/.github/workflows/shell-ci.yml new file mode 100644 index 0000000..35f87cc --- /dev/null +++ b/.github/workflows/shell-ci.yml @@ -0,0 +1,70 @@ +name: Shell CI + +# hl is a single extensionless bash script (shebang-detected, no .sh suffix), +# so every job below targets it by filename rather than a glob. +on: + push: + branches: [main] + paths: + - "hl" + - ".github/workflows/shell-ci.yml" + pull_request: + paths: + - "hl" + - ".github/workflows/shell-ci.yml" + +permissions: + contents: read + +jobs: + shellcheck: + name: Lint (ShellCheck) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # ubuntu-latest ships ShellCheck preinstalled, so no third-party action + # (and its supply-chain risk) is needed for this step. + - name: Run ShellCheck + run: shellcheck --shell=bash --severity=style hl + + shfmt: + name: Format check (shfmt) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: "stable" + + # Pin shfmt to a specific release so the format check doesn't drift + # out from under you when a new shfmt version changes its defaults. + - name: Install shfmt + run: go install mvdan.cc/sh/v3/cmd/shfmt@v3.9.0 + + # -d prints a diff and fails the job instead of rewriting the file. + # Flags here (-i 2 -ci) are a guess at hl's style — run + # `shfmt -i 2 -ci -d hl` locally first and adjust to match, or drop + # them to use shfmt's defaults. + - name: Check formatting + run: shfmt -i 4 -ci -d hl + + security: + name: Security scan (Gitleaks) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # gitleaks needs full history to scan past commits + + # NOTE on pinning: in March 2025, attackers compromised a different + # popular scanning action (tj-actions/changed-files) by repointing + # release tags at malicious commits. Prefer pinning third-party actions + # to a full commit SHA (not just a version tag) and let Dependabot bump + # the SHA for you. Replace the tag below once you've picked/verified one: + # gitleaks/gitleaks-action@ # vX.Y.Z + - name: Run Gitleaks + uses: gitleaks/gitleaks-action@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000..42a89c2 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,41 @@ +# Repo root: zizmor.yml +# (.github/zizmor.yml also works; zizmor discovers either without -c.) +# +# The only rule that needs configuring is unpinned-uses. Out of the box zizmor +# applies a blanket hash-pin policy, so every `uses: someone/action@v4` is a +# High-severity error and the job exits non-zero. +# +# The policy below draws the line where the risk actually is. A git tag is +# mutable: whoever controls the repository can repoint v4 at different code, +# and your next CI run executes it. A commit hash cannot be repointed. +# +# ref-pin a tag or branch is acceptable +# hash-pin a full 40-character commit SHA is required +# any no requirement at all +# +# GitHub's own actions are allowed by tag because you are already trusting +# GitHub to run the job at all; pinning actions/checkout to a hash defends +# against nothing that GitHub could not do anyway. Everything else is a +# third party and gets hash-pinned. + +rules: + unpinned-uses: + config: + policies: + actions/*: ref-pin + github/*: ref-pin + + # TEMPORARY. gitleaks is a third party and should be hash-pinned like + # the rest. This line exists only so CI is not blocked while you get + # the real SHA: + # + # gh api repos/gitleaks/gitleaks-action/commits/v2 --jq .sha + # + # then in python-security.yml write + # uses: gitleaks/gitleaks-action@ # v2 + # and delete this line. `pipx install pinact && pinact run` does the + # same for every action in one pass. + gitleaks/*: ref-pin + + # Everything not listed above. + "*": hash-pin From db0a44bf632fc68be5a5c1b9fc2fea085a92417f Mon Sep 17 00:00:00 2001 From: bytebeast Date: Thu, 30 Jul 2026 12:47:34 -0700 Subject: [PATCH 2/2] feat: hl (AH-2026073062175) Refs: AH-2026073062175 --- hl | 54 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 24 deletions(-) mode change 100755 => 100644 hl diff --git a/hl b/hl old mode 100755 new mode 100644 index 998e667..d731428 --- a/hl +++ b/hl @@ -60,11 +60,11 @@ set -euo pipefail ## the printable characters of escape codes emitted by earlier patterns ## (e.g. '/[0-9]/' matching the "31" inside "\033[0;31m"). ############################################################################### -CLOSE=$'\020' # ends a highlight -MARKS=$'\021\022\023\024\025\026\027\030\031\032\034\035' # one per pattern -SEP=$'\037' # color-list separator -D=$'\036' # sed s/// delimiter -STRIP='\020-\032\034-\037' # scrubbed from input +CLOSE=$'\020' # ends a highlight +MARKS=$'\021\022\023\024\025\026\027\030\031\032\034\035' # one per pattern +SEP=$'\037' # color-list separator +D=$'\036' # sed s/// delimiter +STRIP='\020-\032\034-\037' # scrubbed from input MAX_PATTERNS=${#MARKS} @@ -73,7 +73,10 @@ usage() { exit "${1:-0}" } -die() { printf 'hl: %s\n' "$1" >&2; exit 2; } +die() { + printf 'hl: %s\n' "$1" >&2 + exit 2 +} ############################################################################### ## Color setup (tput or ANSI) @@ -123,21 +126,21 @@ fi ############################################################################### get_color() { case "$1" in - black) printf '%s' "$BLACK" ;; - red) printf '%s' "$RED" ;; - green) printf '%s' "$GREEN" ;; - yellow) printf '%s' "$YELLOW" ;; - blue) printf '%s' "$BLUE" ;; - magenta) printf '%s' "$MAGENTA" ;; - cyan) printf '%s' "$CYAN" ;; - white) printf '%s' "$WHITE" ;; - - bright_red | bred) printf '%s' "$BRIGHT_RED" ;; - bright_green | bgreen) printf '%s' "$BRIGHT_GREEN" ;; + black) printf '%s' "$BLACK" ;; + red) printf '%s' "$RED" ;; + green) printf '%s' "$GREEN" ;; + yellow) printf '%s' "$YELLOW" ;; + blue) printf '%s' "$BLUE" ;; + magenta) printf '%s' "$MAGENTA" ;; + cyan) printf '%s' "$CYAN" ;; + white) printf '%s' "$WHITE" ;; + + bright_red | bred) printf '%s' "$BRIGHT_RED" ;; + bright_green | bgreen) printf '%s' "$BRIGHT_GREEN" ;; bright_yellow | byellow) printf '%s' "$BRIGHT_YELLOW" ;; - bright_blue | bblue) printf '%s' "$BRIGHT_BLUE" ;; + bright_blue | bblue) printf '%s' "$BRIGHT_BLUE" ;; bright_magenta | bmagenta) printf '%s' "$BRIGHT_MAGENTA" ;; - bright_cyan | bcyan) printf '%s' "$BRIGHT_CYAN" ;; + bright_cyan | bcyan) printf '%s' "$BRIGHT_CYAN" ;; *) return 1 ;; esac @@ -157,7 +160,10 @@ escape_bre() { ############################################################################### ## Parse arguments ############################################################################### -[[ $# -eq 0 ]] && { [[ -t 0 ]] && usage 0; exec cat; } +[[ $# -eq 0 ]] && { + [[ -t 0 ]] && usage 0 + exec cat +} case "${1:-}" in -h | --help) usage 0 ;; @@ -205,10 +211,10 @@ done ## sed pass, then resolve markers to ANSI codes with a color stack so nested ## highlights restore the enclosing color instead of clearing it. ############################################################################### -tr -d "$STRIP" \ - | sed "${sed_args[@]}" \ - | awk -v marks="$MARKS" -v cl="$CLOSE" -v sep="$SEP" \ - -v cols="$colors" -v reset="$RESET" ' +tr -d "$STRIP" | + sed "${sed_args[@]}" | + awk -v marks="$MARKS" -v cl="$CLOSE" -v sep="$SEP" \ + -v cols="$colors" -v reset="$RESET" ' BEGIN { split(cols, C, sep); re = "[" marks cl "]" } { line = $0; out = ""; d = 0