From e7859720570477b19be0d81252a2f4c4a966df18 Mon Sep 17 00:00:00 2001 From: Elliot Mackenzie <6545046+barfle@users.noreply.github.com> Date: Fri, 7 Aug 2026 18:02:14 +1000 Subject: [PATCH] Harden open-source access and protection docs. Add SECURITY.md and CODEOWNERS, and document rulesets that require staff CODEOWNER review with no public write path. --- .github/BRANCH_PROTECTION_RULESETS.md | 103 ++++++-------------------- .github/CODEOWNERS | 2 + .github/ruleset-main.json | 52 +++++++++---- .github/ruleset-release.json | 52 +++++++++---- .github/ruleset-tags.json | 22 ++++++ SECURITY.md | 15 ++++ 6 files changed, 137 insertions(+), 109 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ruleset-tags.json create mode 100644 SECURITY.md diff --git a/.github/BRANCH_PROTECTION_RULESETS.md b/.github/BRANCH_PROTECTION_RULESETS.md index 35e06c3..5078598 100644 --- a/.github/BRANCH_PROTECTION_RULESETS.md +++ b/.github/BRANCH_PROTECTION_RULESETS.md @@ -1,91 +1,36 @@ -# Branch protection rulesets +# Branch and tag protection -This repository uses two GitHub branch protection rulesets. +This repository uses GitHub repository rulesets. Public contributors may open pull requests from forks; they do not receive write access. -## Per-repo rules +## Access -1. **Default branch (main)** - - Target: branch name `main`. - - Require a pull request, stale review dismissal, and resolved review threads (self-approval allowed; no mandatory external approval). - - Require status checks: **Analyze (python)**, **Unit tests (3.11)**, **Unit tests (3.12)**, **Compile + help smoke (macos-latest, 3.11)**, **Compile + help smoke (windows-latest, 3.11)**, **No build artifacts tracked**. - - Require linear history. - - Block force pushes and branch deletion. - - Bypass: none configured in rulesets. +- Organisation default repository permission: **none** +- Write/maintain access is limited to internal staff teams (currently `@wildfoundry/dataplicity-web-developers` and related Dataplicity developer teams) +- No outside collaborators +- Direct pushes to protected refs are blocked by rulesets (no bypass actors) -2. **Release branches** - - Target: branch pattern `release/*`. - - Same rules as above (PR + thread resolution + strict required checks + linear history + no force push + no deletion; self-approval allowed). +## Protect main / release/* -## Write access scope +`.github/ruleset-main.json` and `.github/ruleset-release.json` require: -- Rulesets protect branch behavior, but **repository write access** is controlled by repository/org membership and role assignments. -- Keep write access restricted to internal staff by granting write/admin roles only to internal users/teams. +- Pull request before merge +- At least one approving review, including a CODEOWNERS review +- Stale review dismissal and last-push approval +- Resolved review threads +- Required CI status checks +- Linear history +- No force pushes and no branch deletion -## Branch vs repo deletion +## Protect version tags -- **Branches (e.g. main)**: The **deletion** rule in these rulesets protects the targeted branches. Only users with bypass permission (e.g. repo admins) can delete `main` or `release/*`. -- **Whole repository**: Branch protection does **not** protect against deleting the entire repo. Limit organization/repository deletion permissions and keep repo admin access narrow. +`.github/ruleset-tags.json` protects `v*` tags from deletion and force-updates. -## Required status check names - -- Use check names exactly as they appear on pull requests. In this repo, required checks are: - - **Analyze (python)** - - **Unit tests (3.11)** - - **Unit tests (3.12)** - - **Compile + help smoke (macos-latest, 3.11)** - - **Compile + help smoke (windows-latest, 3.11)** - - **No build artifacts tracked** - -## Optional: apply via API - -From the repo root, with `gh` authenticated: +## Apply or refresh via API ```bash -REPO="wildfoundry/dataplicity-cli" -CONTEXTS='[ - {"context":"Analyze (python)"}, - {"context":"Unit tests (3.11)"}, - {"context":"Unit tests (3.12)"}, - {"context":"Compile + help smoke (macos-latest, 3.11)"}, - {"context":"Compile + help smoke (windows-latest, 3.11)"}, - {"context":"No build artifacts tracked"} -]' - -# Ruleset: protect main -gh api "repos/${REPO}/rulesets" -X POST -f name="Protect main" \ - -f target=branch \ - -f enforcement=active \ - -F 'conditions[ref_name][include]=refs/heads/main' \ - -f 'rules[0][type]=pull_request' \ - -F 'rules[0][parameters][required_approving_review_count]=0' \ - -F 'rules[0][parameters][dismiss_stale_reviews_on_push]=true' \ - -F 'rules[0][parameters][require_code_owner_review]=false' \ - -F 'rules[0][parameters][require_last_push_approval]=false' \ - -F 'rules[0][parameters][required_review_thread_resolution]=true' \ - -f 'rules[1][type]=required_status_checks' \ - -F 'rules[1][parameters][strict_required_status_checks_policy]=true' \ - -F "rules[1][parameters][required_status_checks]=${CONTEXTS}" \ - -f 'rules[2][type]=required_linear_history' \ - -f 'rules[3][type]=non_fast_forward' \ - -f 'rules[4][type]=deletion' - -# Ruleset: protect release/* -gh api "repos/${REPO}/rulesets" -X POST -f name="Protect release branches" \ - -f target=branch \ - -f enforcement=active \ - -F 'conditions[ref_name][include]=refs/heads/release/*' \ - -f 'rules[0][type]=pull_request' \ - -F 'rules[0][parameters][required_approving_review_count]=0' \ - -F 'rules[0][parameters][dismiss_stale_reviews_on_push]=true' \ - -F 'rules[0][parameters][require_code_owner_review]=false' \ - -F 'rules[0][parameters][require_last_push_approval]=false' \ - -F 'rules[0][parameters][required_review_thread_resolution]=true' \ - -f 'rules[1][type]=required_status_checks' \ - -F 'rules[1][parameters][strict_required_status_checks_policy]=true' \ - -F "rules[1][parameters][required_status_checks]=${CONTEXTS}" \ - -f 'rules[2][type]=required_linear_history' \ - -f 'rules[3][type]=non_fast_forward' \ - -f 'rules[4][type]=deletion' +REPO=wildfoundry/dataplicity-cli +# Update existing Protect main ruleset ID as needed +gh api --method PUT "repos/${REPO}/rulesets/" --input .github/ruleset-main.json +gh api --method PUT "repos/${REPO}/rulesets/" --input .github/ruleset-release.json +gh api --method POST "repos/${REPO}/rulesets" --input .github/ruleset-tags.json ``` - -If GitHub rejects form-encoded ruleset fields, submit a single JSON body using `.github/ruleset-main.json` and `.github/ruleset-release.json` with `gh api --input`. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..d8c73a1 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Internal maintainers only. External contributors open PRs from forks. +* @wildfoundry/dataplicity-web-developers diff --git a/.github/ruleset-main.json b/.github/ruleset-main.json index 8cb7b0a..93b4a2a 100644 --- a/.github/ruleset-main.json +++ b/.github/ruleset-main.json @@ -2,20 +2,32 @@ "name": "Protect main", "target": "branch", "enforcement": "active", + "bypass_actors": [], "conditions": { "ref_name": { - "include": ["refs/heads/main"], + "include": [ + "refs/heads/main" + ], "exclude": [] } }, "rules": [ + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "required_linear_history" + }, { "type": "pull_request", "parameters": { "dismiss_stale_reviews_on_push": true, - "require_code_owner_review": false, - "require_last_push_approval": false, - "required_approving_review_count": 0, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, "required_review_thread_resolution": true } }, @@ -23,18 +35,28 @@ "type": "required_status_checks", "parameters": { "strict_required_status_checks_policy": true, + "do_not_enforce_on_create": false, "required_status_checks": [ - { "context": "Analyze (python)" }, - { "context": "Unit tests (3.11)" }, - { "context": "Unit tests (3.12)" }, - { "context": "Compile + help smoke (macos-latest, 3.11)" }, - { "context": "Compile + help smoke (windows-latest, 3.11)" }, - { "context": "No build artifacts tracked" } + { + "context": "Analyze (python)" + }, + { + "context": "Unit tests (3.11)" + }, + { + "context": "Unit tests (3.12)" + }, + { + "context": "Compile + help smoke (macos-latest, 3.11)" + }, + { + "context": "Compile + help smoke (windows-latest, 3.11)" + }, + { + "context": "No build artifacts tracked" + } ] } - }, - { "type": "required_linear_history" }, - { "type": "non_fast_forward" }, - { "type": "deletion" } + } ] -} +} \ No newline at end of file diff --git a/.github/ruleset-release.json b/.github/ruleset-release.json index 8297b42..5b82bbf 100644 --- a/.github/ruleset-release.json +++ b/.github/ruleset-release.json @@ -2,20 +2,32 @@ "name": "Protect release branches", "target": "branch", "enforcement": "active", + "bypass_actors": [], "conditions": { "ref_name": { - "include": ["refs/heads/release/*"], + "include": [ + "refs/heads/release/*" + ], "exclude": [] } }, "rules": [ + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "required_linear_history" + }, { "type": "pull_request", "parameters": { "dismiss_stale_reviews_on_push": true, - "require_code_owner_review": false, - "require_last_push_approval": false, - "required_approving_review_count": 0, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, "required_review_thread_resolution": true } }, @@ -23,18 +35,28 @@ "type": "required_status_checks", "parameters": { "strict_required_status_checks_policy": true, + "do_not_enforce_on_create": false, "required_status_checks": [ - { "context": "Analyze (python)" }, - { "context": "Unit tests (3.11)" }, - { "context": "Unit tests (3.12)" }, - { "context": "Compile + help smoke (macos-latest, 3.11)" }, - { "context": "Compile + help smoke (windows-latest, 3.11)" }, - { "context": "No build artifacts tracked" } + { + "context": "Analyze (python)" + }, + { + "context": "Unit tests (3.11)" + }, + { + "context": "Unit tests (3.12)" + }, + { + "context": "Compile + help smoke (macos-latest, 3.11)" + }, + { + "context": "Compile + help smoke (windows-latest, 3.11)" + }, + { + "context": "No build artifacts tracked" + } ] } - }, - { "type": "required_linear_history" }, - { "type": "non_fast_forward" }, - { "type": "deletion" } + } ] -} +} \ No newline at end of file diff --git a/.github/ruleset-tags.json b/.github/ruleset-tags.json new file mode 100644 index 0000000..b41f097 --- /dev/null +++ b/.github/ruleset-tags.json @@ -0,0 +1,22 @@ +{ + "name": "Protect version tags", + "target": "tag", + "enforcement": "active", + "bypass_actors": [], + "conditions": { + "ref_name": { + "include": [ + "refs/tags/v*" + ], + "exclude": [] + } + }, + "rules": [ + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + } + ] +} \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..81ad546 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported versions + +Security fixes are applied to the latest published release of Dataplicity CLI. + +## Reporting a vulnerability + +Please report security issues privately through GitHub Security Advisories: + +https://github.com/wildfoundry/dataplicity-cli/security/advisories/new + +Do not open a public issue for vulnerabilities. Include reproduction steps, affected versions, and impact. + +We aim to acknowledge reports within a few business days.