diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c65b263..4bf76a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,16 +3,16 @@ # Standalone CI: no dependency on third-party actions or external-repo # reusable workflows. The OCaml toolchain is self-hosted via apt + opam # (replacing ocaml/setup-ocaml), and only first-party `actions/*` are used -# (checkout / setup-node / upload-artifact), referenced by upstream major -# tag. dune-project requires OCaml >= 4.14, satisfied by the runner's apt -# OCaml (ocaml-system), with a base-compiler fallback. +# (checkout / setup-node / upload-artifact), SHA-pinned. dune-project requires +# OCaml >= 4.14, satisfied by the runner's apt OCaml (ocaml-system), with a +# base-compiler fallback. # -# NOTE on pins: the previous SHA pins carried fictional version comments -# (`actions/checkout # v6.0.3`, `actions/upload-artifact # v7.0.1` — versions -# that do not exist upstream), so they were re-pointed to real major tags. -# Re-pin to verified upstream SHAs if the repo's SHA-pinning policy requires -# it (these are GitHub-first-party actions, always permitted under any -# "allowed actions" policy). +# NOTE on pins: first-party `actions/*` stay SHA-pinned (repo SHA-pinning +# policy + Hypatia workflow_audit + the "allowed actions" policy that rejects +# tag refs at run-creation). The SHAs are unchanged from the prior ci.yml; +# only the fictional version *comments* (`# v6.0.3`, `# v7.0.1` — versions +# that do not exist upstream) were corrected. checkout's SHA is the same one +# scorecard-enforcer.yml labels `# v4`. name: CI on: push: @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4 - name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml) run: | sudo apt-get update @@ -48,7 +48,7 @@ jobs: || opam switch create . ocaml-base-compiler.4.14.2 --no-install --yes opam exec -- ocaml -version - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4 with: node-version: "20" - name: Install dependencies @@ -99,7 +99,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4 - name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml) run: | sudo apt-get update @@ -128,7 +128,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4 - name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml) run: | sudo apt-get update @@ -164,7 +164,7 @@ jobs: } >> "$GITHUB_STEP_SUMMARY" - name: Upload bench log if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4 with: name: bench-output path: bench-output.log @@ -178,7 +178,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4 - name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml) run: | sudo apt-get update @@ -213,7 +213,7 @@ jobs: } >> "$GITHUB_STEP_SUMMARY" - name: Upload coverage HTML if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4 with: name: coverage-html path: _coverage @@ -238,9 +238,9 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4 - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4 with: node-version: "20" - name: Install test runner dependencies @@ -283,9 +283,9 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4 - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4 with: node-version: "20" - name: Install tree-sitter CLI diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 73b405a..56f42ae 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -26,7 +26,7 @@ jobs: timeout-minutes: 5 steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4 with: fetch-depth: 0 - name: Fetch base ref (DOC-FORMAT delta) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 2e04215..d62967c 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -27,7 +27,7 @@ jobs: id-token: write steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4 with: persist-credentials: false - name: Run analysis diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index 5ea825e..c6269db 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -24,6 +24,6 @@ jobs: timeout-minutes: 5 steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4 - name: Run standalone secret scan run: ./tools/ci/secret-scan-standalone.sh diff --git a/lib/module_loader.ml b/lib/module_loader.ml index a0a5351..0279329 100644 --- a/lib/module_loader.ml +++ b/lib/module_loader.ml @@ -345,61 +345,16 @@ let flatten_imports (loader : t) (prog : program) : program = ) select ) prog.prog_imports in - (* #138: type declarations are a SEPARATE namespace from fn/const bindings, - so they get their own dedup table — a local `fn Foo` must not suppress an - imported `type Foo`, and vice versa. Imported public types are inlined too - so the prog_decls-iterating backends (Deno / JS / Julia / C / Rust / ...) - register their constructors exactly as they would for a local type; - without this, applying an imported constructor (`Some`/`None` from - `use prelude::{Option, Some, None}`) can reach codegen with no type in - scope. Local types win over imported ones, and a type reachable through - more than one path is carried only once. The Wasm backend gets the same - registration natively in [Codegen.gen_imports]. *) - let local_type_names = - List.filter_map (function - | TopType td -> Some td.td_name.name - | _ -> None - ) prog.prog_decls - in - let type_already_in = Hashtbl.create 16 in - List.iter (fun n -> Hashtbl.add type_already_in n ()) local_type_names; - let imported_types = - List.concat_map (fun imp -> - let path_strs path = List.map (fun (id : ident) -> id.name) path in - let mod_path = match imp with - | ImportSimple (p, _) | ImportList (p, _) | ImportGlob p -> path_strs p - in - match Hashtbl.find_opt loader.loaded mod_path with - | None -> [] - | Some lm -> - let public_types = List.filter_map (function - | TopType td when td.td_vis = Public || td.td_vis = PubCrate -> Some td - | _ -> None - ) lm.mod_program.prog_decls in - (* `use M::{..}` selects a type when the list names the type itself or - any of its constructors; `use M` / `use M::*` bring all public - types (the resolver still gates what is referenceable). *) - let selected = match imp with - | ImportGlob _ | ImportSimple _ -> public_types - | ImportList (_, items) -> - let wanted = List.map (fun (it : import_item) -> it.ii_name.name) items in - List.filter (fun td -> - List.mem td.td_name.name wanted || - (match td.td_body with - | TyEnum variants -> - List.exists (fun vd -> List.mem vd.vd_name.name wanted) variants - | _ -> false) - ) public_types - in - List.filter_map (fun td -> - if Hashtbl.mem type_already_in td.td_name.name then None - else begin - Hashtbl.add type_already_in td.td_name.name (); - Some (TopType td) - end - ) selected - ) prog.prog_imports - in - (* Types precede imported fns/consts and all local decls so the single-pass - codegen registers an imported type before any function that uses it. *) - { prog with prog_decls = imported_types @ imported_decls @ prog.prog_decls } + (* #138 follow-up: imported TYPE decls are intentionally NOT inlined here. + An earlier #138 revision carried imported public [TopType]s so the + prog_decls-iterating backends could register their constructors — but the + non-Wasm backends (Deno-ESM / JS / Julia / C / Rust / ...) already emit + the Option/Result constructors from a built-in runtime preamble, so + carrying the prelude types declared `Some`/`None`/`Ok`/`Err` twice + (`SyntaxError: Identifier 'Some' has already been declared` when the + emitted Deno-ESM module is run under node). The Wasm backend learns + imported variant tags natively via [Codegen.gen_imports] (it consumes the + original, un-flattened [prog]); the other backends rely on their preamble. + Re-introducing type-carrying for *user-defined* cross-module enums would + need per-backend constructor dedup first. *) + { prog with prog_decls = imported_decls @ prog.prog_decls } diff --git a/tools/ci/secret-scan-standalone.sh b/tools/ci/secret-scan-standalone.sh index c5aa456..17b7cb8 100755 --- a/tools/ci/secret-scan-standalone.sh +++ b/tools/ci/secret-scan-standalone.sh @@ -12,16 +12,19 @@ # CodeQL + Semgrep remain the deeper SAST layers. Exit non-zero on any hit. set -uo pipefail -# High-confidence credential patterns (low false-positive). +# High-confidence credential patterns (low false-positive). The PEM marker is +# assembled from fragments so this scanner does not itself trip credential +# scanners (no full marker literal appears anywhere in this file). +pem_b="-----BEG""IN" +pem_k="PRIV""ATE KEY-----" patterns=( - '-----BEGIN [A-Z ]*PRIVATE KEY-----' # PEM private keys - 'AKIA[0-9A-Z]{16}' # AWS access key id - 'ASIA[0-9A-Z]{16}' # AWS temporary access key id - 'gh[pousr]_[A-Za-z0-9]{36,}' # GitHub personal/oauth/server tokens - 'github_pat_[A-Za-z0-9_]{40,}' # GitHub fine-grained PAT - 'xox[baprs]-[A-Za-z0-9-]{10,}' # Slack tokens - 'AIza[0-9A-Za-z_-]{35}' # Google API key - '-----BEGIN OPENSSH PRIVATE KEY-----' # OpenSSH private key + "${pem_b} [A-Z ]*${pem_k}" # PEM / OpenSSH private keys + 'AKIA[0-9A-Z]{16}' # AWS access key id + 'ASIA[0-9A-Z]{16}' # AWS temporary access key id + 'gh[pousr]_[A-Za-z0-9]{36,}' # GitHub personal/oauth/server tokens + 'github_pat_[A-Za-z0-9_]{40,}' # GitHub fine-grained PAT + 'xox[baprs]-[A-Za-z0-9-]{10,}' # Slack tokens + 'AIza[0-9A-Za-z_-]{35}' # Google API key ) # Tracked files only, excluding build/vendor output and this script itself @@ -33,7 +36,9 @@ mapfile -t files < <(git ls-files \ hits=0 for pat in "${patterns[@]}"; do if [ "${#files[@]}" -gt 0 ]; then - matches=$(printf '%s\0' "${files[@]}" | xargs -0 -r grep -InE "$pat" 2>/dev/null || true) + # `-e "$pat"` is required: several patterns begin with '-' (PEM markers), + # which grep would otherwise parse as options (silently matching nothing). + matches=$(printf '%s\0' "${files[@]}" | xargs -0 -r grep -InE -e "$pat" 2>/dev/null || true) if [ -n "$matches" ]; then printf '::error::potential secret (pattern: %s)\n' "$pat" printf '%s\n' "$matches" | sed 's/^/ /'