Table of Contents

Repository Host Security Controls

This article documents the GitHub-host controls used to protect the AsiBackbone source and release path. These controls protect repository changes and supply-chain workflow entry points; they do not add runtime security behavior to the AsiBackbone.* packages.

The repository currently operates under the bootstrap solo-maintainer model documented in GOVERNANCE.md and .github/CODEOWNERS. The control set therefore avoids pretending that self-review is independent review while still requiring a pull-request trail, release-blocking CI, and an explicit emergency-bypass path.

Canonical desired state

The machine-readable main-branch ruleset is committed at eng/repository-controls/main-branch-ruleset.json. The live GitHub configuration is expected to match it.

Control Selected posture Rationale
Secret scanning Enabled Detect supported credentials committed to repository history.
Secret-scanning push protection Required and enabled Block supported secrets before they land in the repository. A real secret should be removed/revoked rather than bypassed.
Non-provider patterns Enable when the repository/plan exposes the control Generic private keys and credential-bearing connection strings are relevant to a public .NET repository. Unsupported availability is reported as a warning, not treated as a reason to disable provider push protection.
Validity checks Enable when the repository/plan exposes the control Validity information improves alert triage for supported provider tokens. GitHub may contact the issuing service to determine validity, so this is kept as an explicit recorded decision.
Main-branch ruleset Active Makes the effective control set reviewable instead of relying only on legacy branch-protection defaults.
Pull request before merge Required All ordinary and emergency changes retain a PR/audit trail.
Required approvals 0 during bootstrap solo-maintainer operation GitHub does not permit an author to supply independent approval of their own PR. Requiring one approval with one active maintainer would create a permanent self-lock or force routine bypass.
Code Owner approval Not required during bootstrap solo-maintainer operation .github/CODEOWNERS still records ownership and review routing. Re-enable required Code Owner review when a second active maintainer can provide independent approval.
Last-push approval Not required during bootstrap solo-maintainer operation This control also requires a second person. Revisit with the review-count decision.
Review-thread resolution Required Blocking review conversations must be resolved before ordinary merge.
Merge method Squash only Keeps main linear and ensures normal GitHub merges create one reviewable merge result.
Linear history Required Prevents merge commits from being pushed to main.
Force push and deletion Blocked Protects stable history from destructive updates.
Required signed commits Deferred GitHub evaluates commits introduced by a PR; unsigned local feature-branch commits can block squash merge even when GitHub would sign the final squash commit. The current local/Visual Studio workflow is not yet consistently signed. See the compensating controls below.
Emergency bypass @cdcavell only, pull-request mode only Replaces a broad implicit administrator exemption with one repository-specific, auditable bypass actor that still must use a PR.

Required status checks

The ruleset requires these release-blocking GitHub Actions checks:

  • Dependency review
  • Build, test, and pack
  • CodeQL analysis
  • Validate version consistency
  • External consumer package smoke test
  • Restore, build, test, docs, pack, and smoke
  • Validate workflows with actionlint
  • Analyze workflows with zizmor
  • Analyze dependencies with OWASP Dependency-Check

The checks are bound to the GitHub Actions integration and use strict status-check policy so the pull request must be validated against the current target branch. Each required workflow reports for every pull request targeting main; required workflows must not use path filters because a skipped workflow cannot satisfy its required context.

Public API baseline validation is also pull-request blocking. It runs inside both the required Build, test, and pack and Restore, build, test, docs, pack, and smoke jobs. The repository intentionally does not expose a separate public API status context because doing so would duplicate the same validation rather than strengthen the gate.

Signed-commit decision and compensating controls

Required commit signatures are intentionally not enabled by this issue. GitHub's signed-commit rule evaluates commits introduced by the pull request, not only the final squash commit. Enabling it before the maintainer's local commits and automation identities are consistently signed would turn normal pull requests into bypass-only merges, weakening rather than strengthening the intended model.

Until local signing is adopted and verified end to end, the compensating controls are:

  • every ordinary change reaches main through a pull request;
  • only squash merge is allowed by the ruleset;
  • all nine release-blocking checks must pass unless the documented emergency bypass is explicitly used;
  • force pushes and deletion are blocked;
  • the emergency bypass is limited to one user and to pull requests only;
  • GitHub's merge result and the ruleset-bypass event remain visible in repository history/Rule Insights.

Revisit required_signatures when local GPG/SSH signing and relevant automation identities have been exercised successfully on a non-default branch. Do not enable it only to make the settings page appear stricter if the practical result is that every normal PR must be bypassed.

Applying or auditing the controls

Repository settings are not versioned by Git, so the patch commits the desired state and the tool used to compare/apply it. The live setting must still be changed through GitHub.

Authenticate GitHub CLI with an account that has repository Administration permission, then run the read-only audit:

gh auth status
./scripts/Manage-RepositorySecurityControls.ps1

Preview mutations before applying them:

./scripts/Manage-RepositorySecurityControls.ps1 -Apply -WhatIf

Apply the repository security settings and create/update the canonical ruleset:

./scripts/Manage-RepositorySecurityControls.ps1 -Apply

The apply path enables secret scanning and push protection first. It then attempts to enable non-provider patterns and validity checks independently; if GitHub does not expose one of those optional controls for the repository/plan, the script warns without weakening mandatory push protection. Finally it creates or updates the named main-branch ruleset and reruns the audit.

The legacy branch-protection rule may remain in place as defense in depth while the ruleset is active. Its administrator exemption is not the canonical bypass mechanism: ordinary administrators are still constrained by the active ruleset, and the ruleset contains only the explicit pull-request-only bypass actor above.

Project automation GitHub App

The project-status workflows use a dedicated GitHub App installation token rather than a long-lived personal access token. The App token is minted only for the job that needs it and is passed directly to GitHub CLI through GH_TOKEN.

The workflow-level permissions: block applies only to the built-in GITHUB_TOKEN; it does not constrain a GitHub App installation token. Both project automation workflows therefore use permissions: {} and request only the App permissions required for their GraphQL operations.

Required App installation and permissions

Create a dedicated GitHub App for AsiBackbone project automation and install it on the AsiBackbone organization with repository access limited to AsiBackbone.

Grant only these App permissions:

  • Organization permissions:
    • Projects: Read and write
  • Repository permissions:
    • Issues: Read-only
    • Pull requests: Read-only
    • Metadata: Read-only (GitHub grants this baseline permission)

The pull-request status workflow requests Projects write, Issues read, and Pull requests read. The branch-status workflow requests only Projects write and Issues read. Neither workflow requests repository contents write, administration, or other unrelated permissions.

Configure these repository values:

Type Name Purpose
Repository variable PROJECT_APP_CLIENT_ID GitHub App client ID. This identifier is not secret.
Repository secret PROJECT_APP_PRIVATE_KEY PEM private key used only to mint short-lived installation tokens.

Keep the existing project configuration variables:

  • PROJECT_OWNER
  • PROJECT_NUMBER
  • PROJECT_REVIEW_STATUS
  • PROJECT_IN_PROGRESS_STATUS

Do not store an installation access token as a repository or organization secret. actions/create-github-app-token creates a short-lived token for each job, and the workflows pass that value directly to gh through GH_TOKEN.

Dependabot and fork pull requests

The pull-request project automation deliberately skips Dependabot pull requests and pull requests whose head repository is a fork. Those event types do not receive repository Actions secrets under the normal GitHub security model, so they must not attempt to mint the project App token.

Skipping the project-status mutation does not skip normal validation of those pull requests. It only prevents the credentialed organization-project mutation from running in an event context that cannot receive the App private key.

The branch-status workflow runs on create events in this repository and also excludes dependabot/ branches. A branch created only in an external fork does not create a branch in AsiBackbone/AsiBackbone and therefore does not receive this repository's project-automation credential.

Migration from PROJECT_TOKEN

After the GitHub App is installed and both project workflows have completed successfully:

  1. Delete the legacy PROJECT_TOKEN repository secret.
  2. Revoke the personal access token that previously backed that secret.
  3. Confirm no other repository or organization automation still depends on the PAT before removing any associated authorization.

There is no PAT fallback in the project-status workflows. A future fallback requires separate security review and must not silently restore a broad, long-lived classic PAT.

Rotation and validation

Rotate the App private key by generating a new key in the GitHub App settings, updating PROJECT_APP_PRIVATE_KEY, validating both automation workflows, and then deleting the previous key from the App. For emergency revocation, delete the active private key or suspend/uninstall the App installation.

After configuration or rotation, validate both behavioral paths:

  1. create an issue-<number>-work branch and confirm the corresponding project item moves to PROJECT_IN_PROGRESS_STATUS;
  2. open a non-draft pull request with Closes #<number> and confirm the linked issue moves to PROJECT_REVIEW_STATUS;
  3. confirm a Dependabot pull request and a fork pull request do not run the credential-minting step;
  4. run the repository workflow-security checks, including actionlint and zizmor.

Emergency bypass procedure

Bypass is for an urgent failure of the repository control plane, not a shortcut around an inconvenient test.

  1. Open or retain a pull request. Direct push to main is not the emergency path.
  2. Record which rule/check is being bypassed and why waiting for repair is more dangerous than merging the reviewed change.
  3. Keep the change as narrow as possible and use squash merge.
  4. Never use ruleset bypass as a substitute for secret-scanning push-protection remediation. Remove the secret, revoke/rotate it when appropriate, and retry.
  5. After the emergency merge, repair the failing control and rerun Manage-RepositorySecurityControls.ps1.
  6. Preserve the PR and Rule Insights/bypass event as the audit trail.

If a second active Core Maintainer is appointed, the first repository-control change should reevaluate the bypass list, required approval count, Code Owner review, and last-push approval before the bootstrap exception is retired.

Automation compatibility check

The ruleset targets refs/heads/main; release tags are not targeted. Package publishing, provenance/attestation, and release workflows triggered by version tags therefore keep their existing tag path. Normal pull requests run all nine required checks recorded in the canonical ruleset.

After first applying or materially changing the live controls:

  1. run the read-only repository-control audit;
  2. open a normal PR and confirm all required checks are present;
  3. confirm the PR can merge normally by squash after checks pass;
  4. confirm the resulting main commit is recorded as expected;
  5. run/observe Stable Release Validation and the external consumer smoke workflow;
  6. on the next planned release, verify the tag-triggered package/release path still runs without requiring a branch-ruleset bypass.

GitHub documents repository rulesets, pull-request-only bypass actors, signed commit behavior, and secret-scanning push protection in its repository and code security documentation. The committed manifest is the AsiBackbone-specific decision record; GitHub documentation remains authoritative for platform semantics.