Skip to content

security(automation): isolate hourly commercial-readiness GitHub CLI subprocess authority #108

Description

@seonghobae

Problem

Protected main at c85d710804139c0697d7ef8fa47d02b1389e6d84 runs scripts/hourly-commercial-readiness.mjs from a maintainer-App-backed workflow. On protected source, runGh() calls spawnSync("gh", ...) without an explicit env, so Node forwards the complete ambient process environment to every GitHub CLI child process.

The workflow needs only the dedicated maintainer App token for gh requests; unrelated workflow/job/runner/model/provider/configuration authority must not cross that subprocess boundary merely because it exists in the parent environment.

Active test-first repair

PR #109 (fix/security: isolate hourly readiness gh environment) is the current direct-main repair.

  • protected/live base: c85d710804139c0697d7ef8fa47d02b1389e6d84;
  • RED head: e76d8be9023d5dfb344890cf7250e7f17ee65c04;
  • current GREEN head: 05d6a0ae0f2c9d59d2557679c25db0ddbcdfa822;
  • PR is Ready for review and GitHub reports the content graph mergeable;
  • changed files are limited to scripts/hourly-commercial-readiness.mjs and test/hourly-commercial-readiness-script.test.ts.

The RED regression injects a hostile synthetic parent containing GITHUB_TOKEN, NVIDIA NIM, Maintainer/Reviewer App private material, Cloudflare/provider state, proxy variables, HOME, NODE_OPTIONS, reviewer configuration and unrelated NOEMA_* state. It requires the gh child to receive only optional non-empty PATH, explicit GH_TOKEN, pinned GH_HOST=github.com, and deterministic NO_COLOR=1; an empty parent retains only the pinned non-secret defaults.

Application CI run 31436718995, job 93612364591, checked out exact RED head e76d8be9023d5dfb344890cf7250e7f17ee65c04 and failed exactly the new contract: the environment builder did not exist and runGh() did not pass an explicit child environment. The remaining tests passed.

GREEN 05d6a0ae0f2c9d59d2557679c25db0ddbcdfa822 exports the narrow builder and passes it to shell-free spawnSync without changing pagination, bounded output, exact-head/live-base validation, review authentication, merge decisions, workflow permissions, release authority, deployment authority, licensing posture or central .github policy.

For unchanged GREEN head:

  • application ci 31436885124: terminal success; 62/62 files, 647/647 tests, configured owned production statements/branches/functions/lines all 100%;
  • reviewer-ci 31436885115: terminal success;
  • central Security Scan 31436885127: terminal success under current central revision 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba / security-scan.yml blob c3b8fa5dbbb21a684209683a3d4b6d29c8e0d536;
  • the two code-quality findings emitted on the deliberately RED predecessor are resolved on the GREEN lineage; current inline review threads are 0;
  • current CodeRabbit exact-head review was triggered when fix(security): isolate hourly readiness gh environment #109 left Draft and is still processing, so it is non-passing pending/model evidence;
  • visible formal review evidence is not a qualifying independent APPROVED review.

RCA

Immediate cause: spawnSync inherits process.env when env is omitted.

Root cause: the hourly maintenance adapter treated parent-process configuration and GitHub CLI child authority as the same trust domain rather than constructing an explicit least-authority subprocess contract.

Control gap: the old test forbade a token variable name in source but did not prove which environment variables actually crossed the child-process boundary. The repair replaces that source-text proxy with a behavior-level authority contract.

Acceptance

Implemented on PR #109 exact GREEN head

  • Add a realistic regression around an exported/testable GitHub CLI subprocess-environment builder using a hostile synthetic parent environment.
  • Preserve only the runtime keys required by the reviewed child contract: optional PATH, explicit GH_TOKEN, pinned GH_HOST=github.com, and deterministic NO_COLOR=1.
  • Exclude ambient GITHUB_TOKEN, NVIDIA_NIM_API_KEY, Maintainer/Reviewer App private material, Cloudflare/provider credentials, reviewer configuration, proxy variables, HOME, NODE_OPTIONS, and unrelated NOEMA_* state from the gh child.
  • Replace the old literal-GH_TOKEN source assertion with a behavior-level child-authority contract.
  • Keep shell: false, bounded output, exact-head/live-base checks, reviewer/authentication separation and existing governance semantics unchanged.
  • Obtain full application, exact configured coverage, reviewer-ci and eligible central Security Scan terminal success on the exact GREEN head.

Remaining protected integration / operation

  • Let the current exact-head automated review finish and repair any valid current finding test-first; pending/model-only evidence is not passing.
  • Obtain actual live-governance and qualifying independent formal-review evidence required by chore(governance): protect main and enforce release checks #27/chore(operations): provision and activate hourly maintainer App #29 without self-approval, synthetic status or policy weakening.
  • Merge only an unchanged exact head after every applicable required gate remains satisfied.
  • Re-run the behavior contract from protected main after integration and retain protected-source operational evidence separately from PR/check/model evidence.

Remedy constraints

Do not broaden workflow permissions, introduce a fallback GITHUB_TOKEN, rely on HOME/global gh credentials, copy private keys into subprocess arguments/environment, add a repair/self-modifying workflow, change central .github policy, choose an outbound license, or promote technical GREEN to production/acquisition readiness.

Related: #27, #29, #80, #109, #110

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions