From b779a9a11a99626b8ae9a25fac2ec29382e52288 Mon Sep 17 00:00:00 2001 From: 4444jPPP Date: Sat, 14 Nov 2026 09:08:50 -0500 Subject: [PATCH 1/2] feat: formal GITHUB_TOKEN permission and PR-trigger semantics from pinned GitHub Docs Executable model plus a source-bound PROPOSED ledger (30 occurrences, 34 propositions) with 38 conformance cases; derives the receipt action's fork and pull_request_target limits. Co-Authored-By: Claude Sonnet 5.5 --- README.md | 2 +- docs/github-token-permission-semantics.md | 108 ++ .../github-token-permissions/accounting.json | 31 + .../github-token-permissions/annotations.json | 1428 +++++++++++++++++ .../github-token-permissions/conformance.json | 887 ++++++++++ .../occurrences.jsonl | 30 + .../propositions.jsonl | 34 + ges/__main__.py | 5 + ges/token_semantics.py | 428 +++++ tests/test_token_semantics.py | 216 +++ 10 files changed, 3168 insertions(+), 1 deletion(-) create mode 100644 docs/github-token-permission-semantics.md create mode 100644 evidence/semantics/github-token-permissions/accounting.json create mode 100644 evidence/semantics/github-token-permissions/annotations.json create mode 100644 evidence/semantics/github-token-permissions/conformance.json create mode 100644 evidence/semantics/github-token-permissions/occurrences.jsonl create mode 100644 evidence/semantics/github-token-permissions/propositions.jsonl create mode 100644 ges/token_semantics.py create mode 100644 tests/test_token_semantics.py diff --git a/README.md b/README.md index ed6b933e..5c57f2f5 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ Unresolved dependencies and conditional/version rendering remain explicit review ## Where to look -`controls/catalog.json` is the canonical authored control definition. `generated/functional-checklist.md`, `source-crosswalk.md` and `bindings.json` are generated from it. `templates/` contains implementation starting points; `profiles/` contains explicitly draft target policies. `ges/` owns acquisition, assessment and compilation behavior. `tests/` exercises false-pass prevention and deterministic outputs. `schemas/receipts/` and [agent verification receipts](docs/agent-verification-receipts.md) define the strict Staged/Merged/Verified receipt, its validator and the PR-body action. `docs/` records architecture, limitations, decisions and acceptance criteria. `evidence/` records executed work, not aspirations. +`controls/catalog.json` is the canonical authored control definition. `generated/functional-checklist.md`, `source-crosswalk.md` and `bindings.json` are generated from it. `templates/` contains implementation starting points; `profiles/` contains explicitly draft target policies. `ges/` owns acquisition, assessment and compilation behavior. `tests/` exercises false-pass prevention and deterministic outputs. `schemas/receipts/` and [agent verification receipts](docs/agent-verification-receipts.md) define the strict Staged/Merged/Verified receipt, its validator and the PR-body action. [GITHUB_TOKEN permission semantics](docs/github-token-permission-semantics.md) formalize pinned GitHub Docs passages as an executable model with a source-bound proposed ledger. `docs/` records architecture, limitations, decisions and acceptance criteria. `evidence/` records executed work, not aspirations. The large source corpus and page-level review queues are workflow artifacts and a separate downloadable evidence bundle; they are not duplicated into every consumer repository. diff --git a/docs/github-token-permission-semantics.md b/docs/github-token-permission-semantics.md new file mode 100644 index 00000000..8648827f --- /dev/null +++ b/docs/github-token-permission-semantics.md @@ -0,0 +1,108 @@ +# GITHUB_TOKEN permission semantics from GitHub Docs + +This tranche turns a bounded set of GitHub Docs passages into formal semantics: an +executable model (`ges/token_semantics.py`) plus a source-bound ledger +(`evidence/semantics/github-token-permissions/`). It is the first GitHub Docs slice +that is both extracted and executable; it does not review the 13,219-file corpus +(see [GitHub Docs source scope](github-docs-source-scope.md)). + +Scope: how the `GITHUB_TOKEN` gets its permissions, how fork runs and the two +pull-request triggers change them, token lifetime, which events a token-made change +re-triggers, and what `GITHUB_SHA`/`GITHUB_REF` denote for the two pull-request +triggers. These are the rules the [agent verification receipt](agent-verification-receipts.md) +action depends on (it writes a pull request body with the job token). + +## Status and limits + +Every record is `PROPOSED` with no reviewer. Structural validity, source binding and +model conformance do not certify that the prose was read correctly, and the +documentation is not a specification of GitHub's server behaviour. No policy is +adopted, no control accepted, and no live GitHub behaviour was observed. Where the +prose is silent or hedged the model says so (see Ambiguities) rather than guessing. + +Source: `github/docs` at `56fcfa816f27bca239e5d39fff0d4f74f77ec995` (the commit in +`sources/sources.lock.json`), nine files, 30 occurrences, 34 propositions. 30 +propositions name a model rule; 4 are preserved without one. The ledger stores +hashes and formalized ASTs, not the prose. + +## The model + +Access levels are ordered `none < read < write`. Scopes and their allowed levels come +from the docs' permission list; `id-token` allows `write|none` (fpt/ghec), +`vulnerability-alerts` allows `read|none`, and four scopes appear only under named +`ifversion` flags. + +| Rule | Statement | Function | +| --- | --- | --- | +| T3 | The token is valid until the job finishes or the runner's maximum lifetime: 6 h on GitHub-hosted runners, refreshable for at most 24 h on self-hosted | `token_is_valid` | +| T4 | A `permissions` value assigns `read`, `write` or `none` per scope; `write` includes `read`; disallowed levels are invalid | `apply_permissions_key`, `allows` | +| T5 | Specifying any scope sets every unspecified scope to `none` | `apply_permissions_key` | +| T6 | `read-all` / `write-all` give every scope that level, clipped to the scope's allowed levels | `apply_permissions_key` | +| T7 | `permissions: {}` sets every scope to `none` | `apply_permissions_key` | +| T8 | Calculation: enterprise/organization/repository default, then workflow-level `permissions`, then job-level `permissions`, then (pull request event other than `pull_request_target`, from a fork, `Send write tokens to workflows from pull requests` off) every `write` becomes `read` | `resolve_token_permissions` | +| T9 | `permissions` may sit at workflow level or on a job; a job's key replaces the workflow's for that job | `resolve_token_permissions` | +| T10 | In a fork run the key can add or remove `read` but typically not grant `write` | `resolve_token_permissions` | +| T11 | The admin setting above is the exception that allows fork `write` | `resolve_token_permissions` | +| T12 | `pull_request_target` runs are not downgraded, even from a public fork | `resolve_token_permissions` | +| T13 | From a fork, no secret except `GITHUB_TOKEN` reaches the runner | `secrets_reach_runner` | +| E1 | `pull_request`: `GITHUB_SHA` is the merge commit, `GITHUB_REF` is `refs/pull/N/merge`; skipped on a merge conflict | `pull_request_event_context` | +| E2 | Both triggers run by default for `opened`, `synchronize`, `reopened` | `pull_request_event_context` | +| E3 | Events caused by the repository token create no run, except `workflow_dispatch`/`repository_dispatch` (always) and `pull_request` `opened`/`synchronize`/`reopened` (approval-required, behind the docs' version flag) | `workflow_run_for_token_event` | +| E4 | `pull_request_target` runs against the default branch of the base repository (base branch on GHES before 3.20), never the merge commit | `pull_request_event_context` | + +The default access map is a caller input. The pinned pages do not enumerate the +restricted and permissive presets, so the model does not invent them; the conformance +cases use `contents: read, packages: read` as an illustration. + +## Conformance + +`evidence/semantics/github-token-permissions/conformance.json` holds 38 cases. Each +cites rules; `check_conformance` fails when the model disagrees with a case, when a +case cites a rule no source statement supports, and when a rule that source statements +support has no case. Cases include the error results the model must produce (for +example `read` for `id-token`). + +``` +python -m ges token-semantics conformance +python -m ges token-semantics validate +python -m ges token-semantics verify --sources PINNED_DOCS_CHECKOUT +python -m ges token-semantics build --sources PINNED_DOCS_CHECKOUT [--output DIR] +``` + +`build` and `verify` need the nine pinned files laid out as in the `github/docs` +checkout; `verify` rebuilds the ledger and fails on any difference, so a changed +content digest, a moved line range or a missing anchor phrase is reported. CI exercises +the same code on a synthetic source (`tests/test_token_semantics.py`); the real files +are not vendored. + +## Consequences for the receipt action + +Derived by the model and asserted in tests: + +- A fork `pull_request` run cannot hold `pull-requests: write` by default, so the action's + body update fails visibly there, as documented. With the admin setting on it can write. +- A `pull_request_target` run keeps `write` from a fork, which is why the action refuses + that event: the token is writable while the receipt would come from untrusted context. +- `GITHUB_SHA` on `pull_request` is the merge commit, so a receipt `head_sha` must come + from the event payload (`github.event.pull_request.head.sha`), as the action requires. +- The action's own `PATCH` of the body uses the job token and its `edited` event creates no + workflow run (E3), so the action cannot recurse. + +## Ambiguities preserved + +- T12: whether `pull_request_target` overrides a restricted default or only skips the fork + downgrade is unstated; the weaker reading is implemented. +- T6/T8: `id-token` has no `read`; `read-all` and the fork downgrade give it `none`. The + docs do not say this. +- T11: "typically you can't" is not defined; the setting is treated as the only exception. +- The docs state the `pull_request` payload "is empty for merged pull requests and pull + requests that come from forked repositories". It conflicts with common use of the + payload and is preserved without a rule; verify against a real run before relying on it. +- The `pull_request_target` branch-name pattern restriction is preserved without a rule: + the patterns are not given. + +## Not covered + +The remaining GitHub Docs corpus, the GitHub App permission tables, REST and webhook +reference data, org-level default-permission pages, reviewer approval of these +propositions, and any comparison with live GitHub behaviour. diff --git a/evidence/semantics/github-token-permissions/accounting.json b/evidence/semantics/github-token-permissions/accounting.json new file mode 100644 index 00000000..78e920cc --- /dev/null +++ b/evidence/semantics/github-token-permissions/accounting.json @@ -0,0 +1,31 @@ +{ + "commit": "56fcfa816f27bca239e5d39fff0d4f74f77ec995", + "files": 9, + "occurrences": 30, + "policy_adopted": false, + "propositions": 34, + "propositions_preserved_without_model_rule": 4, + "propositions_with_model_rule": 30, + "repository": "github/docs", + "review_status": "PROPOSED", + "rules_supported": [ + "E1", + "E2", + "E3", + "E4", + "T10", + "T11", + "T12", + "T13", + "T3", + "T4", + "T5", + "T6", + "T7", + "T8", + "T9" + ], + "schema": "ges.token-semantics-accounting.v1", + "scope": "Selected GITHUB_TOKEN permission and pull-request trigger passages only; not a review of the GitHub Docs corpus.", + "semantic_truth_certified": false +} diff --git a/evidence/semantics/github-token-permissions/annotations.json b/evidence/semantics/github-token-permissions/annotations.json new file mode 100644 index 00000000..6cfe9ada --- /dev/null +++ b/evidence/semantics/github-token-permissions/annotations.json @@ -0,0 +1,1428 @@ +{ + "commit": "56fcfa816f27bca239e5d39fff0d4f74f77ec995", + "entries": [ + { + "conditions": [], + "content_sha256": "e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8", + "end_line": 15, + "expect": "unique `GITHUB_TOKEN` secret", + "heading_ancestry": [ + "About the `GITHUB_TOKEN`" + ], + "include_chain": [], + "path": "content/actions/concepts/security/github_token.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "DESCRIPTIVE: forall job j, a distinct GITHUB_TOKEN is created when j starts and can authenticate j.", + "preserved_differences": [], + "rules": [], + "semantic_ast": { + "action": "create", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "a unique GITHUB_TOKEN secret at the start of each workflow job", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/concepts/security/github_token.md" + ], + "subject": "GitHub" + } + } + ], + "semantic_kind": "FACT", + "start_line": 15, + "versions": [ + "fpt", + "ghes", + "ghec" + ] + }, + { + "conditions": [], + "content_sha256": "e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8", + "end_line": 17, + "expect": "installation access token", + "heading_ancestry": [ + "About the `GITHUB_TOKEN`" + ], + "include_chain": [], + "path": "content/actions/concepts/security/github_token.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "DESCRIPTIVE: token(j).repository = workflow(j).repository; token is an installation access token of the GitHub App installed on that repository.", + "preserved_differences": [], + "rules": [], + "semantic_ast": { + "action": "be", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "a GitHub App installation access token limited to the repository that contains the workflow", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [ + "Installed when GitHub Actions is enabled" + ], + "scope": [ + "github/docs", + "content/actions/concepts/security/github_token.md" + ], + "subject": "GITHUB_TOKEN" + } + } + ], + "semantic_kind": "FACT", + "start_line": 17, + "versions": [ + "fpt", + "ghes", + "ghec" + ] + }, + { + "conditions": [], + "content_sha256": "e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8", + "end_line": 19, + "expect": "expires when the job finishes", + "heading_ancestry": [ + "About the `GITHUB_TOKEN`" + ], + "include_chain": [], + "path": "content/actions/concepts/security/github_token.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "DESCRIPTIVE: valid(token(j), t) iff t < min(end(j), start(j) + lifetime(runner(j))).", + "preserved_differences": [], + "rules": [ + "T3" + ], + "semantic_ast": { + "action": "expire", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "when the job finishes or after its effective maximum lifetime", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [ + "GitHub fetches an installation access token before each job begins" + ], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/concepts/security/github_token.md" + ], + "subject": "GITHUB_TOKEN" + } + } + ], + "semantic_kind": "FACT", + "start_line": 19, + "versions": [ + "fpt", + "ghes", + "ghec" + ] + }, + { + "conditions": [], + "content_sha256": "e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8", + "end_line": 24, + "expect": "24 hours", + "heading_ancestry": [ + "About the `GITHUB_TOKEN`" + ], + "include_chain": [], + "path": "content/actions/concepts/security/github_token.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "DESCRIPTIVE: lifetime(github-hosted) <= 6h because maximum job execution time is 6h.", + "preserved_differences": [], + "rules": [ + "T3" + ], + "semantic_ast": { + "action": "live", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "at most 6 hours on GitHub-hosted runners", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [ + "GitHub-hosted runner" + ], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/concepts/security/github_token.md" + ], + "subject": "GITHUB_TOKEN" + } + }, + { + "ambiguities": [], + "formalization": "DESCRIPTIVE: lifetime(self-hosted) <= 24h; job duration up to 5 days does not extend it.", + "preserved_differences": [], + "rules": [ + "T3" + ], + "semantic_ast": { + "action": "refresh", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "for at most 24 hours on self-hosted runners although jobs may run 5 days", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [ + "Self-hosted runner" + ], + "qualifiers": [ + "Use a personal access token or other authentication for jobs longer than 24 hours" + ], + "scope": [ + "github/docs", + "content/actions/concepts/security/github_token.md" + ], + "subject": "GITHUB_TOKEN" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 23, + "versions": [ + "fpt", + "ghes", + "ghec" + ] + }, + { + "conditions": [], + "content_sha256": "2077c4672482dd746c60cd2f75e62e394dd668b74fdd73338d5a895523fafdef", + "end_line": 3, + "expect": "workflow_dispatch", + "heading_ancestry": [ + "When `GITHUB_TOKEN` triggers workflow runs" + ], + "include_chain": [ + "content/actions/concepts/security/github_token.md" + ], + "path": "data/reusables/actions/actions-do-not-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "NOT RUN unless event in {workflow_dispatch, repository_dispatch}.", + "preserved_differences": [], + "rules": [ + "E3" + ], + "semantic_ast": { + "action": "create", + "consequences": [], + "exceptions": [ + "workflow_dispatch and repository_dispatch events always create workflow runs" + ], + "modality": "DESCRIPTIVE", + "object": "a new workflow run", + "parameters": [], + "polarity": "NEGATIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/actions-do-not-trigger-workflows.md" + ], + "subject": "events triggered by the repository GITHUB_TOKEN" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 1, + "versions": [] + }, + { + "conditions": [ + "ifversion actions-github-token-pull-request-approval" + ], + "content_sha256": "2077c4672482dd746c60cd2f75e62e394dd668b74fdd73338d5a895523fafdef", + "end_line": 4, + "expect": "approval-required", + "heading_ancestry": [ + "When `GITHUB_TOKEN` triggers workflow runs" + ], + "include_chain": [ + "content/actions/concepts/security/github_token.md" + ], + "path": "data/reusables/actions/actions-do-not-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "APPROVAL_REQUIRED iff event = pull_request and activity in {opened, synchronize, reopened} and flag actions-github-token-pull-request-approval; labeled, edited, closed => NO_RUN.", + "preserved_differences": [], + "rules": [ + "E3" + ], + "semantic_ast": { + "action": "create", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "workflow runs in an approval-required state", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [ + "A user with write access can start the runs by selecting Approve workflows to run", + "Other pull_request activity types do not create workflow runs" + ], + "scope": [ + "github/docs", + "data/reusables/actions/actions-do-not-trigger-workflows.md" + ], + "subject": "pull_request events with opened, synchronize or reopened activity types caused by GITHUB_TOKEN" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 4, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "2077c4672482dd746c60cd2f75e62e394dd668b74fdd73338d5a895523fafdef", + "end_line": 6, + "expect": "recursive", + "heading_ancestry": [ + "When `GITHUB_TOKEN` triggers workflow runs" + ], + "include_chain": [ + "content/actions/concepts/security/github_token.md" + ], + "path": "data/reusables/actions/actions-do-not-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "NOT RUN: example instance of the general rule for all other events.", + "preserved_differences": [], + "rules": [ + "E3" + ], + "semantic_ast": { + "action": "create", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "a new push-triggered workflow run", + "parameters": [], + "polarity": "NEGATIVE", + "preconditions": [], + "qualifiers": [ + "Prevents accidental recursive workflow runs" + ], + "scope": [ + "github/docs", + "data/reusables/actions/actions-do-not-trigger-workflows.md" + ], + "subject": "a push made with the repository GITHUB_TOKEN" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 6, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "482aa4bf0e2ad3061611c010143168a79cf1664126951c177722f6352662481e", + "end_line": 1, + "expect": "forked repository", + "heading_ancestry": [], + "include_chain": [], + "path": "data/reusables/actions/forked-secrets.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "reaches_runner(name, fork) iff name is GITHUB_TOKEN or not fork.", + "preserved_differences": [], + "rules": [ + "T13" + ], + "semantic_ast": { + "action": "pass", + "consequences": [], + "exceptions": [ + "GITHUB_TOKEN" + ], + "modality": "DESCRIPTIVE", + "object": "secrets other than GITHUB_TOKEN to the runner", + "parameters": [], + "polarity": "NEGATIVE", + "preconditions": [ + "Workflow triggered from a forked repository" + ], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/forked-secrets.md" + ], + "subject": "GitHub Actions" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 1, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "5c090d30bda583faa17770216757dd1c1d006de72c9dec49a11fc34960111347", + "end_line": 1, + "expect": "Send write tokens to workflows from pull requests", + "heading_ancestry": [ + "`permissions`", + "Changing the permissions in a forked repository" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/forked-write-permission.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "In a fork run, permissions can add or remove read; write cannot be granted.", + "preserved_differences": [], + "rules": [ + "T10" + ], + "semantic_ast": { + "action": "add or remove", + "consequences": [], + "exceptions": [], + "modality": "MAY", + "object": "read permissions for forked repositories with the permissions key", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/forked-write-permission.md" + ], + "subject": "workflow author" + } + }, + { + "ambiguities": [ + "\"typically\" is not defined; the model treats the setting as the only exception" + ], + "formalization": "write grantable in fork runs iff admin selected Send write tokens to workflows from pull requests.", + "preserved_differences": [], + "rules": [ + "T11" + ], + "semantic_ast": { + "action": "grant", + "consequences": [], + "exceptions": [ + "An admin selected Send write tokens to workflows from pull requests" + ], + "modality": "MAY", + "object": "write access in a forked repository with the permissions key", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [ + "The docs say \"typically you can't\", so the normative strength is hedged" + ], + "scope": [ + "github/docs", + "data/reusables/actions/forked-write-permission.md" + ], + "subject": "workflow author" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 1, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33", + "end_line": 1, + "expect": "read`, `write`, or `none`", + "heading_ancestry": [ + "Defining access for the `GITHUB_TOKEN` scopes" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/github-token-available-permissions.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "level(scope) in {none, read, write}, subject to per-scope restrictions.", + "preserved_differences": [], + "rules": [ + "T4" + ], + "semantic_ast": { + "action": "accept", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "read, write or none as the value of each available permission", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/github-token-available-permissions.md" + ], + "subject": "permissions key" + } + } + ], + "semantic_kind": "DEFINITION", + "start_line": 1, + "versions": [] + }, + { + "conditions": [ + "ifversion fpt or ghec" + ], + "content_sha256": "f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33", + "end_line": 12, + "expect": "write|none", + "heading_ancestry": [ + "Defining access for the `GITHUB_TOKEN` scopes" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/github-token-available-permissions.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "id-token in {write, none}; read is not a level.", + "preserved_differences": [], + "rules": [ + "T4" + ], + "semantic_ast": { + "action": "accept", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "only write or none", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/github-token-available-permissions.md", + "id-token" + ], + "subject": "id-token permission" + } + } + ], + "semantic_kind": "DEFINITION", + "start_line": 11, + "versions": [] + }, + { + "conditions": [ + "ifversion vulnerability-alerts-permission" + ], + "content_sha256": "f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33", + "end_line": 21, + "expect": "vulnerability-alerts: read|none", + "heading_ancestry": [ + "Defining access for the `GITHUB_TOKEN` scopes" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/github-token-available-permissions.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "vulnerability-alerts in {read, none}; write is not valid.", + "preserved_differences": [], + "rules": [ + "T4" + ], + "semantic_ast": { + "action": "accept", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "only read or none", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/github-token-available-permissions.md", + "vulnerability-alerts" + ], + "subject": "vulnerability-alerts permission" + } + } + ], + "semantic_kind": "DEFINITION", + "start_line": 20, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33", + "end_line": 24, + "expect": "not specified are set to `none`", + "heading_ancestry": [ + "Defining access for the `GITHUB_TOKEN` scopes" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/github-token-available-permissions.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "if spec is a nonempty mapping then forall s not in dom(spec): level(s) = none.", + "preserved_differences": [], + "rules": [ + "T5" + ], + "semantic_ast": { + "action": "set", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "every unspecified permission to none once any permission is specified", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/github-token-available-permissions.md", + "permissions" + ], + "subject": "GITHUB_TOKEN" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 24, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33", + "end_line": 34, + "expect": "read-all", + "heading_ancestry": [ + "Defining access for the `GITHUB_TOKEN` scopes" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/github-token-available-permissions.md", + "propositions": [ + { + "ambiguities": [ + "The page does not say what read-all gives id-token, which has no read level; the model grants the highest allowed level not above the request, so none" + ], + "formalization": "read-all: forall s level(s) = read; write-all: forall s level(s) = write; clipped to the levels the scope allows.", + "preserved_differences": [ + "write-all on vulnerability-alerts is read, per the scope description reusable" + ], + "rules": [ + "T6" + ], + "semantic_ast": { + "action": "define", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "read or write access for all of the available permissions", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/github-token-available-permissions.md", + "permissions" + ], + "subject": "permissions: read-all or write-all" + } + } + ], + "semantic_kind": "DEFINITION", + "start_line": 26, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33", + "end_line": 40, + "expect": "{}", + "heading_ancestry": [ + "Defining access for the `GITHUB_TOKEN` scopes" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/github-token-available-permissions.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "spec = {} => forall s level(s) = none.", + "preserved_differences": [], + "rules": [ + "T7" + ], + "semantic_ast": { + "action": "disable", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "permissions for all of the available permissions", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/github-token-available-permissions.md", + "permissions" + ], + "subject": "permissions: {}" + } + } + ], + "semantic_kind": "DEFINITION", + "start_line": 36, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "098a6fd06de8b6f3b535d24634c15a43ae438d7012f38266579af011b341e4d0", + "end_line": 1, + "expect": "`write` includes `read`", + "heading_ancestry": [ + "Defining access for the `GITHUB_TOKEN` scopes" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/github-token-scope-descriptions.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "none < read < write; holding write satisfies a read requirement.", + "preserved_differences": [], + "rules": [ + "T4" + ], + "semantic_ast": { + "action": "include", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "read access", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/github-token-scope-descriptions.md" + ], + "subject": "write access level" + } + }, + { + "ambiguities": [], + "formalization": "Repeats the unspecified-is-none rule at the scope table.", + "preserved_differences": [], + "rules": [ + "T5" + ], + "semantic_ast": { + "action": "set", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "unspecified permissions to none when any is specified", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/github-token-scope-descriptions.md", + "permissions" + ], + "subject": "GITHUB_TOKEN" + } + } + ], + "semantic_kind": "DEFINITION", + "start_line": 1, + "versions": [] + }, + { + "conditions": [ + "ifversion vulnerability-alerts-permission" + ], + "content_sha256": "098a6fd06de8b6f3b535d24634c15a43ae438d7012f38266579af011b341e4d0", + "end_line": 31, + "expect": "automatically included as `read`", + "heading_ancestry": [ + "Defining access for the `GITHUB_TOKEN` scopes" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/github-token-scope-descriptions.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "read-all and write-all both map vulnerability-alerts to read.", + "preserved_differences": [], + "rules": [ + "T6" + ], + "semantic_ast": { + "action": "include", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "vulnerability-alerts as read", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/github-token-scope-descriptions.md", + "vulnerability-alerts" + ], + "subject": "read-all and write-all" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 31, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "bb7922e3b52455bef820b309489a59c7e09f4621a8f097756763f77de47a3a77", + "end_line": 3, + "expect": "top-level key", + "heading_ancestry": [ + "`permissions`" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "job permissions apply to every action and run command in that job that uses the token.", + "preserved_differences": [], + "rules": [ + "T9" + ], + "semantic_ast": { + "action": "place", + "consequences": [], + "exceptions": [], + "modality": "MAY", + "object": "permissions at the top level to apply to all jobs or within a specific job", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md" + ], + "subject": "workflow author" + } + } + ], + "semantic_kind": "CAPABILITY", + "start_line": 3, + "versions": [] + }, + { + "conditions": [ + "ifversion not fpt" + ], + "content_sha256": "bb7922e3b52455bef820b309489a59c7e09f4621a8f097756763f77de47a3a77", + "end_line": 5, + "expect": "restrict write access", + "heading_ancestry": [ + "`permissions`" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "Organization/enterprise defaults can lower the starting access before workflow-level adjustment.", + "preserved_differences": [], + "rules": [ + "T8" + ], + "semantic_ast": { + "action": "restrict", + "consequences": [], + "exceptions": [], + "modality": "MAY", + "object": "write access for the GITHUB_TOKEN at the repository level", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [ + "Enterprise owners apply outside fpt" + ], + "scope": [ + "github/docs", + "data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md" + ], + "subject": "organization owners" + } + } + ], + "semantic_kind": "CAPABILITY", + "start_line": 5, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "bb7922e3b52455bef820b309489a59c7e09f4621a8f097756763f77de47a3a77", + "end_line": 7, + "expect": "pull_request_target", + "heading_ancestry": [ + "`permissions`" + ], + "include_chain": [ + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "path": "data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md", + "propositions": [ + { + "ambiguities": [ + "Whether this overrides a restricted default or only means the fork downgrade is not applied is not stated; the model implements the weaker reading" + ], + "formalization": "event = pull_request_target is exempt from the fork downgrade of rule T8.", + "preserved_differences": [], + "rules": [ + "T12" + ], + "semantic_ast": { + "action": "be granted", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "read/write repository permission when the workflow is triggered by pull_request_target, even from a public fork", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md" + ], + "subject": "GITHUB_TOKEN" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 7, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "50c73cf50995a0c7641988b1f72ca9e3447ce2920f4a48cb468d7765eff0b6f1", + "end_line": 354, + "expect": "initially set to the default setting", + "heading_ancestry": [ + "`permissions`", + "How permissions are calculated for a workflow job" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/workflow-syntax.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "perm = default; if workflow.permissions then perm = apply(workflow); if job.permissions then perm = apply(job); then fork adjustment.", + "preserved_differences": [], + "rules": [ + "T8" + ], + "semantic_ast": { + "action": "calculate", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "job token permissions starting from the enterprise, organization or repository default, then workflow level, then job level", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "subject": "GitHub" + } + }, + { + "ambiguities": [ + "A write permission on a scope with no read level (id-token) has no read to fall to; the model sets it to none" + ], + "formalization": "if event in pull_request events minus pull_request_target and fork and not send_write_tokens then write -> read.", + "preserved_differences": [], + "rules": [ + "T8", + "T10" + ], + "semantic_ast": { + "action": "change", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "every write permission to read only after calculation", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [ + "Workflow triggered by a pull request event other than pull_request_target from a forked repository", + "Send write tokens to workflows from pull requests is not selected" + ], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/workflow-syntax.md" + ], + "subject": "GitHub" + } + } + ], + "semantic_kind": "PROCEDURE", + "start_line": 354, + "versions": [ + "fpt", + "ghes", + "ghec" + ] + }, + { + "conditions": [], + "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", + "end_line": 462, + "expect": "Last merge commit", + "heading_ancestry": [ + "`pull_request`" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "sha(pull_request) = merge commit, not the pull request head commit.", + "preserved_differences": [], + "rules": [ + "E1" + ], + "semantic_ast": { + "action": "set", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "GITHUB_SHA to the last merge commit on the GITHUB_REF branch and GITHUB_REF to refs/pull/PULL_REQUEST_NUMBER/merge", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [ + "Consumers needing the head commit must read it from the event payload" + ], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "pull_request" + ], + "subject": "pull_request event" + } + } + ], + "semantic_kind": "FACT", + "start_line": 462, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", + "end_line": 465, + "expect": "opened`, `synchronize`, or `reopened`", + "heading_ancestry": [ + "`pull_request`" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "default types = {opened, synchronize, reopened}; others require the types keyword.", + "preserved_differences": [], + "rules": [ + "E2" + ], + "semantic_ast": { + "action": "run", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "by default only for opened, synchronize and reopened activity types", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "pull_request" + ], + "subject": "pull_request workflow" + } + } + ], + "semantic_kind": "FACT", + "start_line": 465, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", + "end_line": 466, + "expect": "merge conflict", + "heading_ancestry": [ + "`pull_request`" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "skipped(pull_request) iff merge conflict; skipped(pull_request_target) is false.", + "preserved_differences": [], + "rules": [ + "E1" + ], + "semantic_ast": { + "action": "run", + "consequences": [], + "exceptions": [ + "pull_request_target runs even with a merge conflict" + ], + "modality": "DESCRIPTIVE", + "object": "on a pull request that has a merge conflict", + "parameters": [], + "polarity": "NEGATIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "pull_request" + ], + "subject": "pull_request workflow" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 466, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", + "end_line": 467, + "expect": "payload is empty", + "heading_ancestry": [ + "`pull_request`" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [ + "Contradicts common use of github.event.pull_request fields in same-repository runs and is not modelled; verify against a real run before relying on it" + ], + "formalization": "Preserved without a model rule.", + "preserved_differences": [], + "rules": [], + "semantic_ast": { + "action": "be", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "empty for merged pull requests and pull requests from forked repositories", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "pull_request" + ], + "subject": "pull_request webhook event payload" + } + } + ], + "semantic_kind": "FACT", + "start_line": 467, + "versions": [] + }, + { + "conditions": [ + "ifversion actions-github-token-pull-request-approval" + ], + "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", + "end_line": 468, + "expect": "approval", + "heading_ancestry": [ + "`pull_request`" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "Same rule as the reusable at actions-do-not-trigger-workflows.md; other GITHUB_TOKEN-triggered events create no runs except workflow_dispatch and repository_dispatch.", + "preserved_differences": [], + "rules": [ + "E3" + ], + "semantic_ast": { + "action": "create", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "workflow runs that require approval for opened, synchronize and reopened", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "pull_request" + ], + "subject": "pull_request events caused by a workflow using GITHUB_TOKEN" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 468, + "versions": [] + }, + { + "conditions": [ + "ifversion ghes < 3.20" + ], + "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", + "end_line": 669, + "expect": "Last commit on default branch", + "heading_ancestry": [ + "`pull_request_target`" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "sha(pull_request_target) = base-side commit; never the pull request head or merge commit.", + "preserved_differences": [], + "rules": [ + "E4" + ], + "semantic_ast": { + "action": "set", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "GITHUB_SHA to the last commit on the default branch (the pull request base branch on GHES before 3.20)", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "pull_request_target" + ], + "subject": "pull_request_target event" + } + } + ], + "semantic_kind": "FACT", + "start_line": 665, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", + "end_line": 672, + "expect": "opened`, `synchronize`, or `reopened`", + "heading_ancestry": [ + "`pull_request_target`" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "default types identical to pull_request.", + "preserved_differences": [], + "rules": [ + "E2" + ], + "semantic_ast": { + "action": "run", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "by default only for opened, synchronize and reopened activity types", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "pull_request_target" + ], + "subject": "pull_request_target workflow" + } + } + ], + "semantic_kind": "FACT", + "start_line": 672, + "versions": [] + }, + { + "conditions": [ + "ifversion ghes < 3.20 selects the base of the pull request instead" + ], + "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", + "end_line": 676, + "expect": "rather than in the context of the merge commit", + "heading_ancestry": [ + "`pull_request_target`" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "Prevents execution of unsafe head code; avoid when building or running pull request code.", + "preserved_differences": [], + "rules": [ + "E4" + ], + "semantic_ast": { + "action": "run", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "in the context of the default branch of the base repository rather than the merge commit", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [ + "Avoid when you need to build or run code from the pull request" + ], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "pull_request_target" + ], + "subject": "pull_request_target event" + } + } + ], + "semantic_kind": "FACT", + "start_line": 676, + "versions": [] + }, + { + "conditions": [], + "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", + "end_line": 678, + "expect": "names that match certain patterns", + "heading_ancestry": [ + "`pull_request_target`" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [ + "Patterns are undefined in the source; no behaviour can be derived" + ], + "formalization": "Preserved without a model rule: the patterns are not enumerated.", + "preserved_differences": [], + "rules": [], + "semantic_ast": { + "action": "trigger", + "consequences": [], + "exceptions": [], + "modality": "MAY", + "object": "workflows with pull_request_target", + "parameters": [], + "polarity": "NEGATIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "pull_request_target" + ], + "subject": "branches whose names look similar to SHAs" + } + } + ], + "semantic_kind": "CONSTRAINT", + "start_line": 678, + "versions": [] + } + ], + "repository": "github/docs", + "schema": "ges.token-semantics-annotations.v1" +} diff --git a/evidence/semantics/github-token-permissions/conformance.json b/evidence/semantics/github-token-permissions/conformance.json new file mode 100644 index 00000000..dc0d457a --- /dev/null +++ b/evidence/semantics/github-token-permissions/conformance.json @@ -0,0 +1,887 @@ +{ + "cases": [ + { + "expect": { + "allowed": true + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "push", + "from_fork": false, + "needed": "read", + "scope": "contents", + "workflow": { + "contents": "write" + } + }, + "id": "T4-write-includes-read", + "kind": "can", + "rules": [ + "T4" + ] + }, + { + "expect": { + "allowed": false + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "push", + "from_fork": false, + "needed": "write", + "scope": "contents", + "workflow": { + "contents": "read" + } + }, + "id": "T4-read-excludes-write", + "kind": "can", + "rules": [ + "T4" + ] + }, + { + "expect": { + "error": "scope id-token cannot be set to 'read' (allowed: none|write)" + }, + "given": { + "flags": [ + "fpt-or-ghec" + ], + "spec": { + "id-token": "read" + } + }, + "id": "T4-id-token-read-invalid", + "kind": "permissions-key", + "rules": [ + "T4" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "none", + "deployments": "none", + "discussions": "none", + "id-token": "write", + "issues": "none", + "packages": "none", + "pages": "none", + "pull-requests": "none", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "flags": [ + "fpt-or-ghec" + ], + "spec": { + "id-token": "write" + } + }, + "id": "T4-id-token-write-valid", + "kind": "permissions-key", + "rules": [ + "T4" + ] + }, + { + "expect": { + "error": "unknown or unavailable permission scope: id-token" + }, + "given": { + "spec": { + "id-token": "write" + } + }, + "id": "T4-id-token-gated-off-ghes", + "kind": "permissions-key", + "rules": [ + "T4" + ] + }, + { + "expect": { + "error": "scope vulnerability-alerts cannot be set to 'write' (allowed: none|read)" + }, + "given": { + "flags": [ + "vulnerability-alerts-permission" + ], + "spec": { + "vulnerability-alerts": "write" + } + }, + "id": "T4-vulnerability-alerts-write-invalid", + "kind": "permissions-key", + "rules": [ + "T4" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "none", + "deployments": "none", + "discussions": "none", + "issues": "write", + "packages": "none", + "pages": "none", + "pull-requests": "none", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "push", + "from_fork": false, + "workflow": { + "issues": "write" + } + }, + "id": "T5-unspecified-become-none", + "kind": "resolve", + "rules": [ + "T5" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "read", + "deployments": "none", + "discussions": "none", + "issues": "none", + "packages": "read", + "pages": "none", + "pull-requests": "none", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "push", + "from_fork": false + }, + "id": "T5-absent-key-keeps-default", + "kind": "resolve", + "rules": [ + "T5", + "T8" + ] + }, + { + "expect": { + "permissions": { + "actions": "read", + "checks": "read", + "contents": "read", + "deployments": "read", + "discussions": "read", + "id-token": "none", + "issues": "read", + "packages": "read", + "pages": "read", + "pull-requests": "read", + "security-events": "read", + "statuses": "read", + "vulnerability-alerts": "read" + } + }, + "given": { + "flags": [ + "vulnerability-alerts-permission", + "fpt-or-ghec" + ], + "spec": "read-all" + }, + "id": "T6-read-all", + "kind": "permissions-key", + "rules": [ + "T6" + ] + }, + { + "expect": { + "permissions": { + "actions": "write", + "checks": "write", + "contents": "write", + "deployments": "write", + "discussions": "write", + "id-token": "write", + "issues": "write", + "packages": "write", + "pages": "write", + "pull-requests": "write", + "security-events": "write", + "statuses": "write", + "vulnerability-alerts": "read" + } + }, + "given": { + "flags": [ + "vulnerability-alerts-permission", + "fpt-or-ghec" + ], + "spec": "write-all" + }, + "id": "T6-write-all", + "kind": "permissions-key", + "rules": [ + "T6" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "none", + "deployments": "none", + "discussions": "none", + "issues": "none", + "packages": "none", + "pages": "none", + "pull-requests": "none", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "spec": {} + }, + "id": "T7-empty-mapping-disables-all", + "kind": "permissions-key", + "rules": [ + "T7" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "none", + "deployments": "none", + "discussions": "none", + "issues": "read", + "packages": "none", + "pages": "none", + "pull-requests": "none", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "push", + "from_fork": false, + "job": { + "issues": "read" + }, + "workflow": { + "contents": "write" + } + }, + "id": "T8-job-level-replaces-workflow-level", + "kind": "resolve", + "rules": [ + "T8", + "T9" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "write", + "deployments": "none", + "discussions": "none", + "issues": "none", + "packages": "none", + "pages": "none", + "pull-requests": "none", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "push", + "from_fork": false, + "workflow": { + "contents": "write" + } + }, + "id": "T8-workflow-level-applies-without-job", + "kind": "resolve", + "rules": [ + "T8", + "T9" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "read", + "deployments": "none", + "discussions": "none", + "issues": "none", + "packages": "none", + "pages": "none", + "pull-requests": "read", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "pull_request", + "from_fork": true, + "workflow": { + "contents": "read", + "pull-requests": "write" + } + }, + "id": "T8-fork-pull-request-write-becomes-read", + "kind": "resolve", + "rules": [ + "T8", + "T10" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "read", + "deployments": "none", + "discussions": "none", + "issues": "none", + "packages": "none", + "pages": "none", + "pull-requests": "write", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "pull_request", + "from_fork": false, + "workflow": { + "contents": "read", + "pull-requests": "write" + } + }, + "id": "T8-same-repo-pull-request-keeps-write", + "kind": "resolve", + "rules": [ + "T8" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "none", + "deployments": "none", + "discussions": "none", + "issues": "none", + "packages": "none", + "pages": "none", + "pull-requests": "read", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "pull_request_review", + "from_fork": true, + "workflow": { + "pull-requests": "write" + } + }, + "id": "T8-fork-pull-request-review-also-downgraded", + "kind": "resolve", + "rules": [ + "T8", + "T10" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "none", + "deployments": "none", + "discussions": "none", + "id-token": "none", + "issues": "none", + "packages": "none", + "pages": "none", + "pull-requests": "none", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "pull_request", + "flags": [ + "fpt-or-ghec" + ], + "from_fork": true, + "workflow": { + "id-token": "write" + } + }, + "id": "T8-fork-id-token-write-becomes-none", + "kind": "resolve", + "rules": [ + "T8", + "T10" + ] + }, + { + "expect": { + "permissions": { + "actions": "none", + "checks": "none", + "contents": "write", + "deployments": "none", + "discussions": "none", + "issues": "none", + "packages": "none", + "pages": "none", + "pull-requests": "none", + "security-events": "none", + "statuses": "none" + } + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "workflow_dispatch", + "from_fork": true, + "workflow": { + "contents": "write" + } + }, + "id": "T8-fork-workflow-dispatch-not-downgraded", + "kind": "resolve", + "rules": [ + "T8" + ] + }, + { + "expect": { + "allowed": false + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "pull_request", + "from_fork": true, + "needed": "write", + "scope": "pull-requests", + "workflow": { + "pull-requests": "write" + } + }, + "id": "T10-fork-cannot-grant-write", + "kind": "can", + "rules": [ + "T10" + ] + }, + { + "expect": { + "allowed": true + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "pull_request", + "from_fork": true, + "needed": "read", + "scope": "pull-requests", + "workflow": { + "pull-requests": "read" + } + }, + "id": "T10-fork-read-permission-retained", + "kind": "can", + "rules": [ + "T10" + ] + }, + { + "expect": { + "allowed": true + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "pull_request", + "from_fork": true, + "needed": "write", + "scope": "pull-requests", + "send_write_tokens": true, + "workflow": { + "pull-requests": "write" + } + }, + "id": "T11-send-write-tokens-allows-fork-write", + "kind": "can", + "rules": [ + "T11" + ] + }, + { + "expect": { + "allowed": true + }, + "given": { + "default": { + "contents": "read", + "packages": "read" + }, + "event": "pull_request_target", + "from_fork": true, + "needed": "write", + "scope": "pull-requests", + "workflow": { + "pull-requests": "write" + } + }, + "id": "T12-pull-request-target-fork-keeps-write", + "kind": "can", + "rules": [ + "T12" + ] + }, + { + "expect": { + "reaches_runner": false + }, + "given": { + "from_fork": true, + "name": "DEPLOY_KEY" + }, + "id": "T13-fork-secret-withheld", + "kind": "secret", + "rules": [ + "T13" + ] + }, + { + "expect": { + "reaches_runner": true + }, + "given": { + "from_fork": true, + "name": "GITHUB_TOKEN" + }, + "id": "T13-fork-github-token-passed", + "kind": "secret", + "rules": [ + "T13" + ] + }, + { + "expect": { + "reaches_runner": true + }, + "given": { + "from_fork": false, + "name": "DEPLOY_KEY" + }, + "id": "T13-same-repo-secret-passed", + "kind": "secret", + "rules": [ + "T13" + ] + }, + { + "expect": { + "valid": false + }, + "given": { + "elapsed_hours": 6.0, + "job_finished": false, + "runner": "github-hosted" + }, + "id": "T3-hosted-expires-at-6h", + "kind": "token-valid", + "rules": [ + "T3" + ] + }, + { + "expect": { + "valid": true + }, + "given": { + "elapsed_hours": 5.9, + "job_finished": false, + "runner": "github-hosted" + }, + "id": "T3-hosted-valid-before-6h", + "kind": "token-valid", + "rules": [ + "T3" + ] + }, + { + "expect": { + "valid": false + }, + "given": { + "elapsed_hours": 24.0, + "job_finished": false, + "runner": "self-hosted" + }, + "id": "T3-self-hosted-refresh-capped-24h", + "kind": "token-valid", + "rules": [ + "T3" + ] + }, + { + "expect": { + "valid": false + }, + "given": { + "elapsed_hours": 1.0, + "job_finished": true, + "runner": "self-hosted" + }, + "id": "T3-expires-when-job-finishes", + "kind": "token-valid", + "rules": [ + "T3" + ] + }, + { + "expect": { + "context": { + "default_activity_types": [ + "opened", + "synchronize", + "reopened" + ], + "ref": "refs/pull/PULL_REQUEST_NUMBER/merge", + "runs_against": "merge commit of the pull request", + "sha": "last merge commit on the GITHUB_REF branch", + "skipped_on_merge_conflict": true + } + }, + "given": { + "event": "pull_request" + }, + "id": "E1-pull-request-context", + "kind": "event-context", + "rules": [ + "E1" + ] + }, + { + "expect": { + "context": { + "default_activity_types": [ + "opened", + "synchronize", + "reopened" + ], + "ref": "default branch", + "runs_against": "default branch of the base repository", + "sha": "last commit on default branch", + "skipped_on_merge_conflict": false + } + }, + "given": { + "event": "pull_request_target" + }, + "id": "E2-default-activity-types", + "kind": "event-context", + "rules": [ + "E2" + ] + }, + { + "expect": { + "context": { + "default_activity_types": [ + "opened", + "synchronize", + "reopened" + ], + "ref": "default branch", + "runs_against": "default branch of the base repository", + "sha": "last commit on default branch", + "skipped_on_merge_conflict": false + } + }, + "given": { + "event": "pull_request_target" + }, + "id": "E4-pull-request-target-context", + "kind": "event-context", + "rules": [ + "E4" + ] + }, + { + "expect": { + "context": { + "default_activity_types": [ + "opened", + "synchronize", + "reopened" + ], + "ref": "pull request base branch", + "runs_against": "base of the pull request", + "sha": "last commit on the pull request base branch", + "skipped_on_merge_conflict": false + } + }, + "given": { + "event": "pull_request_target", + "ghes_before_3_20": true + }, + "id": "E4-ghes-before-3-20-base-branch", + "kind": "event-context", + "rules": [ + "E4" + ] + }, + { + "expect": { + "result": "RUN" + }, + "given": { + "event": "workflow_dispatch" + }, + "id": "E3-dispatch-always-runs", + "kind": "token-event", + "rules": [ + "E3" + ] + }, + { + "expect": { + "result": "NO_RUN" + }, + "given": { + "event": "push" + }, + "id": "E3-push-by-token-no-run", + "kind": "token-event", + "rules": [ + "E3" + ] + }, + { + "expect": { + "result": "APPROVAL_REQUIRED" + }, + "given": { + "activity_type": "opened", + "event": "pull_request" + }, + "id": "E3-pull-request-opened-needs-approval", + "kind": "token-event", + "rules": [ + "E3" + ] + }, + { + "expect": { + "result": "NO_RUN" + }, + "given": { + "activity_type": "edited", + "event": "pull_request" + }, + "id": "E3-pull-request-edited-no-run", + "kind": "token-event", + "rules": [ + "E3" + ] + }, + { + "expect": { + "result": "NO_RUN" + }, + "given": { + "activity_type": "opened", + "approval_flag": false, + "event": "pull_request" + }, + "id": "E3-approval-flag-off-no-run", + "kind": "token-event", + "rules": [ + "E3" + ] + } + ], + "default_note": "Defaults are caller-supplied illustrations (contents:read, packages:read); the pinned pages do not enumerate the restricted/permissive presets.", + "schema": "ges.token-semantics-conformance.v1" +} diff --git a/evidence/semantics/github-token-permissions/occurrences.jsonl b/evidence/semantics/github-token-permissions/occurrences.jsonl new file mode 100644 index 00000000..f0005e10 --- /dev/null +++ b/evidence/semantics/github-token-permissions/occurrences.jsonl @@ -0,0 +1,30 @@ +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"DESCRIPTIVE: forall job j, a distinct GITHUB_TOKEN is created when j starts and can authenticate j.","heading_ancestry":["About the `GITHUB_TOKEN`"],"id":"semantic-occurrence:84e693e82c466345bd6f3bc4c1ea870d7f40519f01ab454c9163ff10876d9789","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"a unique GITHUB_TOKEN secret at the start of each workflow job","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GitHub"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8","end_line":15,"path":"content/actions/concepts/security/github_token.md","repository":"github/docs","span_sha256":"e9288a946657f909e989a1e7bc8cafea74d30ec3e0edef90989e813d802d87df","start_line":15},"versions":["fpt","ghes","ghec"]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"DESCRIPTIVE: token(j).repository = workflow(j).repository; token is an installation access token of the GitHub App installed on that repository.","heading_ancestry":["About the `GITHUB_TOKEN`"],"id":"semantic-occurrence:66166e598b41c65fa708a3a1ac2739f60e067b96f49d94c9b7e6e1ee8102ec52","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"be","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"a GitHub App installation access token limited to the repository that contains the workflow","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Installed when GitHub Actions is enabled"],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8","end_line":17,"path":"content/actions/concepts/security/github_token.md","repository":"github/docs","span_sha256":"cf8029260f82f0211bcb5337bf6724a6e2a7e2cf38ba63e9c2915aa2d5ed3d55","start_line":17},"versions":["fpt","ghes","ghec"]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"DESCRIPTIVE: valid(token(j), t) iff t < min(end(j), start(j) + lifetime(runner(j))).","heading_ancestry":["About the `GITHUB_TOKEN`"],"id":"semantic-occurrence:555cabccd7097b33829d9c917405a67290c32f72aa5c23e4b8a82adebf546272","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"expire","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"when the job finishes or after its effective maximum lifetime","parameters":[],"polarity":"POSITIVE","preconditions":["GitHub fetches an installation access token before each job begins"],"qualifiers":[],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8","end_line":19,"path":"content/actions/concepts/security/github_token.md","repository":"github/docs","span_sha256":"755f5722abdd72280c125b4e223b7fe45e00e382cedd2042116eca49ec3cdf7d","start_line":19},"versions":["fpt","ghes","ghec"]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"DESCRIPTIVE: lifetime(github-hosted) <= 6h because maximum job execution time is 6h. DESCRIPTIVE: lifetime(self-hosted) <= 24h; job duration up to 5 days does not extend it.","heading_ancestry":["About the `GITHUB_TOKEN`"],"id":"semantic-occurrence:f7d0309d251bdbf0b7035c5d555dcc566080dab503ce1be84a5bb99c730b6d4d","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"live","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"at most 6 hours on GitHub-hosted runners","parameters":[],"polarity":"POSITIVE","preconditions":["GitHub-hosted runner"],"qualifiers":[],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8","end_line":24,"path":"content/actions/concepts/security/github_token.md","repository":"github/docs","span_sha256":"9aaa82b3af316d70ff41ef6f70be48c23fcb87d0a21cf12064de30b08c40499c","start_line":23},"versions":["fpt","ghes","ghec"]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"NOT RUN unless event in {workflow_dispatch, repository_dispatch}.","heading_ancestry":["When `GITHUB_TOKEN` triggers workflow runs"],"id":"semantic-occurrence:493494a1cbc9c3d281c61a2efa708ef7adc8970329bddebaf4e2b4f1b9cc95ac","include_chain":["content/actions/concepts/security/github_token.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":["workflow_dispatch and repository_dispatch events always create workflow runs"],"modality":"DESCRIPTIVE","object":"a new workflow run","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"events triggered by the repository GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"2077c4672482dd746c60cd2f75e62e394dd668b74fdd73338d5a895523fafdef","end_line":3,"path":"data/reusables/actions/actions-do-not-trigger-workflows.md","repository":"github/docs","span_sha256":"7ef4e8048d16eeab7d8c9324ede62ede8e352a1a04272c463df83030e18c20a7","start_line":1},"versions":[]} +{"authority_class":"PLATFORM","conditions":["ifversion actions-github-token-pull-request-approval"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"APPROVAL_REQUIRED iff event = pull_request and activity in {opened, synchronize, reopened} and flag actions-github-token-pull-request-approval; labeled, edited, closed => NO_RUN.","heading_ancestry":["When `GITHUB_TOKEN` triggers workflow runs"],"id":"semantic-occurrence:eaa6eff3d3bebf5705c804913c97e84d48cfe0fba34501237ac405389e5f86d4","include_chain":["content/actions/concepts/security/github_token.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"workflow runs in an approval-required state","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["A user with write access can start the runs by selecting Approve workflows to run","Other pull_request activity types do not create workflow runs"],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"pull_request events with opened, synchronize or reopened activity types caused by GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"2077c4672482dd746c60cd2f75e62e394dd668b74fdd73338d5a895523fafdef","end_line":4,"path":"data/reusables/actions/actions-do-not-trigger-workflows.md","repository":"github/docs","span_sha256":"73ea36f2060cb14ba33029dfae002a42a1eaef3a25f35209b97a3212a4c873b9","start_line":4},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"NOT RUN: example instance of the general rule for all other events.","heading_ancestry":["When `GITHUB_TOKEN` triggers workflow runs"],"id":"semantic-occurrence:278a06109127ce63a350310b02cb8eb4c464aa1eee57fc386928e3935dd9dd2b","include_chain":["content/actions/concepts/security/github_token.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"a new push-triggered workflow run","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":["Prevents accidental recursive workflow runs"],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"a push made with the repository GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"2077c4672482dd746c60cd2f75e62e394dd668b74fdd73338d5a895523fafdef","end_line":6,"path":"data/reusables/actions/actions-do-not-trigger-workflows.md","repository":"github/docs","span_sha256":"b7f69697e50645460bc03ab1e2f32efcb98b1be1e5b03400439272b7ebb051db","start_line":6},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"reaches_runner(name, fork) iff name is GITHUB_TOKEN or not fork.","heading_ancestry":[],"id":"semantic-occurrence:91d1b848d0dcc627ae4746927e7adfcaaa81af8cabcf68847e5e624efba98458","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"pass","consequences":[],"exceptions":["GITHUB_TOKEN"],"modality":"DESCRIPTIVE","object":"secrets other than GITHUB_TOKEN to the runner","parameters":[],"polarity":"NEGATIVE","preconditions":["Workflow triggered from a forked repository"],"qualifiers":[],"scope":["github/docs","data/reusables/actions/forked-secrets.md"],"subject":"GitHub Actions"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"482aa4bf0e2ad3061611c010143168a79cf1664126951c177722f6352662481e","end_line":1,"path":"data/reusables/actions/forked-secrets.md","repository":"github/docs","span_sha256":"482aa4bf0e2ad3061611c010143168a79cf1664126951c177722f6352662481e","start_line":1},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"In a fork run, permissions can add or remove read; write cannot be granted. write grantable in fork runs iff admin selected Send write tokens to workflows from pull requests.","heading_ancestry":["`permissions`","Changing the permissions in a forked repository"],"id":"semantic-occurrence:4901fefec53a987e9ceb443a03b3d54b79f2fbe9e5e364915d3e0cac32dd9938","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"add or remove","consequences":[],"exceptions":[],"modality":"MAY","object":"read permissions for forked repositories with the permissions key","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/forked-write-permission.md"],"subject":"workflow author"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"5c090d30bda583faa17770216757dd1c1d006de72c9dec49a11fc34960111347","end_line":1,"path":"data/reusables/actions/forked-write-permission.md","repository":"github/docs","span_sha256":"5c090d30bda583faa17770216757dd1c1d006de72c9dec49a11fc34960111347","start_line":1},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"level(scope) in {none, read, write}, subject to per-scope restrictions.","heading_ancestry":["Defining access for the `GITHUB_TOKEN` scopes"],"id":"semantic-occurrence:aa8934c774956f14647220854bef8ca3eafbea0f32d35d3260772dc4d987e0dd","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"accept","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"read, write or none as the value of each available permission","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md"],"subject":"permissions key"},"semantic_kind":"DEFINITION","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33","end_line":1,"path":"data/reusables/actions/github-token-available-permissions.md","repository":"github/docs","span_sha256":"80cb617e60fa38f8eb8f13557563e1a1467da02ceec2cbb782614311b28cf812","start_line":1},"versions":[]} +{"authority_class":"PLATFORM","conditions":["ifversion fpt or ghec"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"id-token in {write, none}; read is not a level.","heading_ancestry":["Defining access for the `GITHUB_TOKEN` scopes"],"id":"semantic-occurrence:3b7f29b1c0f8ae2aadd8d09186874644810cd91d67e7da0f7097cc4ea2f07b41","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"accept","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"only write or none","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md","id-token"],"subject":"id-token permission"},"semantic_kind":"DEFINITION","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33","end_line":12,"path":"data/reusables/actions/github-token-available-permissions.md","repository":"github/docs","span_sha256":"ee854c0b71be99d49b89447addf8bf1c05faeb514ea36f43a9776461714554f1","start_line":11},"versions":[]} +{"authority_class":"PLATFORM","conditions":["ifversion vulnerability-alerts-permission"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"vulnerability-alerts in {read, none}; write is not valid.","heading_ancestry":["Defining access for the `GITHUB_TOKEN` scopes"],"id":"semantic-occurrence:0b1622e76116d1cf40db21a51035838ce3fe6c46390f94e2b0140175258f44e9","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"accept","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"only read or none","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md","vulnerability-alerts"],"subject":"vulnerability-alerts permission"},"semantic_kind":"DEFINITION","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33","end_line":21,"path":"data/reusables/actions/github-token-available-permissions.md","repository":"github/docs","span_sha256":"a8ceb4159038ce3f044127423cb6232c07f55910c5c326b3fedaaa05521bd563","start_line":20},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"if spec is a nonempty mapping then forall s not in dom(spec): level(s) = none.","heading_ancestry":["Defining access for the `GITHUB_TOKEN` scopes"],"id":"semantic-occurrence:54a50d8f9e0387b6ff1267b679fc2574b96c4961ba57bc9983e49956d90b5c0c","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"every unspecified permission to none once any permission is specified","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md","permissions"],"subject":"GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33","end_line":24,"path":"data/reusables/actions/github-token-available-permissions.md","repository":"github/docs","span_sha256":"1dbcf4ebeef9c1b9a5140c6ff9f6e389829402e710672c43a1dcf44f2236fecc","start_line":24},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"read-all: forall s level(s) = read; write-all: forall s level(s) = write; clipped to the levels the scope allows.","heading_ancestry":["Defining access for the `GITHUB_TOKEN` scopes"],"id":"semantic-occurrence:540e05ab0946eae9e58b59bbe7b6341a2549438d0ee0999b3bee79e984857175","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"define","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"read or write access for all of the available permissions","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md","permissions"],"subject":"permissions: read-all or write-all"},"semantic_kind":"DEFINITION","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33","end_line":34,"path":"data/reusables/actions/github-token-available-permissions.md","repository":"github/docs","span_sha256":"19d9ffdebff0ef6de90d9746e41a09b70021cadcb1188d4e740b4e7b1cb896c1","start_line":26},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"spec = {} => forall s level(s) = none.","heading_ancestry":["Defining access for the `GITHUB_TOKEN` scopes"],"id":"semantic-occurrence:944f4daf9bcb74977050a42c6d58ab24581a1a76934babcdc6f6fe0355f25417","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"disable","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"permissions for all of the available permissions","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md","permissions"],"subject":"permissions: {}"},"semantic_kind":"DEFINITION","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33","end_line":40,"path":"data/reusables/actions/github-token-available-permissions.md","repository":"github/docs","span_sha256":"e457c7d22c8d8f0981133a1a84c22bf2b0a46159e0bcf7b4fe596eb60b4cc199","start_line":36},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"none < read < write; holding write satisfies a read requirement. Repeats the unspecified-is-none rule at the scope table.","heading_ancestry":["Defining access for the `GITHUB_TOKEN` scopes"],"id":"semantic-occurrence:85720dad680ebb8d8ccdc57dfc54b4b0378bd3bedb5e5f40d95b0b951babf275","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"include","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"read access","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-scope-descriptions.md"],"subject":"write access level"},"semantic_kind":"DEFINITION","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"098a6fd06de8b6f3b535d24634c15a43ae438d7012f38266579af011b341e4d0","end_line":1,"path":"data/reusables/actions/github-token-scope-descriptions.md","repository":"github/docs","span_sha256":"6be1e6834b3e4e265959b19ac2d4f39e839285815e733aec651b07bb71fa0bf6","start_line":1},"versions":[]} +{"authority_class":"PLATFORM","conditions":["ifversion vulnerability-alerts-permission"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"read-all and write-all both map vulnerability-alerts to read.","heading_ancestry":["Defining access for the `GITHUB_TOKEN` scopes"],"id":"semantic-occurrence:72b4cb0ec04da2073f9acb764127af3540c8b49cac2b05a3751ddd99d82c8182","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"include","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"vulnerability-alerts as read","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-scope-descriptions.md","vulnerability-alerts"],"subject":"read-all and write-all"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"098a6fd06de8b6f3b535d24634c15a43ae438d7012f38266579af011b341e4d0","end_line":31,"path":"data/reusables/actions/github-token-scope-descriptions.md","repository":"github/docs","span_sha256":"4e8003992f4cecdacec22c49c5ebbafcc534211ba233ee470783b628a4e4e286","start_line":31},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"job permissions apply to every action and run command in that job that uses the token.","heading_ancestry":["`permissions`"],"id":"semantic-occurrence:daca0c42613ca7484a3ff79c18d71459406f3ad00a41dddec02adf5c46206604","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"place","consequences":[],"exceptions":[],"modality":"MAY","object":"permissions at the top level to apply to all jobs or within a specific job","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md"],"subject":"workflow author"},"semantic_kind":"CAPABILITY","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"bb7922e3b52455bef820b309489a59c7e09f4621a8f097756763f77de47a3a77","end_line":3,"path":"data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md","repository":"github/docs","span_sha256":"3840798c4cc670eea44b1ef166ea2b9c4372ffee231496c5fe540c6f20bf83e8","start_line":3},"versions":[]} +{"authority_class":"PLATFORM","conditions":["ifversion not fpt"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Organization/enterprise defaults can lower the starting access before workflow-level adjustment.","heading_ancestry":["`permissions`"],"id":"semantic-occurrence:a238189b1f4e33a4da816014508822e54adfeace90a94a9ccf8dd15ee3399788","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"restrict","consequences":[],"exceptions":[],"modality":"MAY","object":"write access for the GITHUB_TOKEN at the repository level","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Enterprise owners apply outside fpt"],"scope":["github/docs","data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md"],"subject":"organization owners"},"semantic_kind":"CAPABILITY","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"bb7922e3b52455bef820b309489a59c7e09f4621a8f097756763f77de47a3a77","end_line":5,"path":"data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md","repository":"github/docs","span_sha256":"2fc742bbecd44396f14f1ed54eb7fc4de49971779454d07704487c049f6d87cb","start_line":5},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"event = pull_request_target is exempt from the fork downgrade of rule T8.","heading_ancestry":["`permissions`"],"id":"semantic-occurrence:de029a00d76869f72db9d2776697dcd5b116098b3c32da600d75828039809ef1","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"be granted","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"read/write repository permission when the workflow is triggered by pull_request_target, even from a public fork","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md"],"subject":"GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"bb7922e3b52455bef820b309489a59c7e09f4621a8f097756763f77de47a3a77","end_line":7,"path":"data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md","repository":"github/docs","span_sha256":"f18d9a40006f3a542da6d3cc3d365fac1e6a85638da05317f985c48c0eca95dc","start_line":7},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"perm = default; if workflow.permissions then perm = apply(workflow); if job.permissions then perm = apply(job); then fork adjustment. if event in pull_request events minus pull_request_target and fork and not send_write_tokens then write -> read.","heading_ancestry":["`permissions`","How permissions are calculated for a workflow job"],"id":"semantic-occurrence:71424a623f0d766af0eb03a1fd752aca1f08654aec7de890311ded4837ad4f0a","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"calculate","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"job token permissions starting from the enterprise, organization or repository default, then workflow level, then job level","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/workflow-syntax.md"],"subject":"GitHub"},"semantic_kind":"PROCEDURE","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"50c73cf50995a0c7641988b1f72ca9e3447ce2920f4a48cb468d7765eff0b6f1","end_line":354,"path":"content/actions/reference/workflows-and-actions/workflow-syntax.md","repository":"github/docs","span_sha256":"feb2a08cc731d9f47798bc93830d90276da967da50c4a086297e9b5214b1b3c9","start_line":354},"versions":["fpt","ghes","ghec"]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"sha(pull_request) = merge commit, not the pull request head commit.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:a8161c3133bb0e9792816592a72c218e00b9071c3bbe654c755e9449ffde1230","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_SHA to the last merge commit on the GITHUB_REF branch and GITHUB_REF to refs/pull/PULL_REQUEST_NUMBER/merge","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Consumers needing the head commit must read it from the event payload"],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request event"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":462,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"315b89dd09cd81f86d786a7db2e486df12cb872e919102aa9b7de324e0f9f1b2","start_line":462},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"default types = {opened, synchronize, reopened}; others require the types keyword.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:5ad49b8e8364302cebec7bf9a5586233002fad28b303cff241e7551fa65d72bc","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"by default only for opened, synchronize and reopened activity types","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request workflow"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":465,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"cfe41b2e44d1c58c50737be3192181408445cf161b23bbda848ccff635b59d40","start_line":465},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"skipped(pull_request) iff merge conflict; skipped(pull_request_target) is false.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:8751bea4bc24f0a3900ef001b9383ce95908c5e5faa693b8619319b7160b8bfe","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":["pull_request_target runs even with a merge conflict"],"modality":"DESCRIPTIVE","object":"on a pull request that has a merge conflict","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request workflow"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":466,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"fd1e85086384062c8bf094330b5add1b5d4ab2dc8ce6437ec4582bd10e49a68a","start_line":466},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Preserved without a model rule.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:bcd282fc5bb4f2faa54a923fb98a13162db65c3329793d248452f490e0d7dc10","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"be","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"empty for merged pull requests and pull requests from forked repositories","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request webhook event payload"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":467,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"6575d80f34020008921c61bbf79f0c50cdadb3364e38014a1c04162356940ae8","start_line":467},"versions":[]} +{"authority_class":"PLATFORM","conditions":["ifversion actions-github-token-pull-request-approval"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Same rule as the reusable at actions-do-not-trigger-workflows.md; other GITHUB_TOKEN-triggered events create no runs except workflow_dispatch and repository_dispatch.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:e686dc1efbb0bdb1235517b83b805ef3448366f10e5bae0ccee3f63c5f509a1f","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"workflow runs that require approval for opened, synchronize and reopened","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request events caused by a workflow using GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":468,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"956ca13dae815053f5453d5b21c647f60a75fdc773f31f16bd309032f8c7a555","start_line":468},"versions":[]} +{"authority_class":"PLATFORM","conditions":["ifversion ghes < 3.20"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"sha(pull_request_target) = base-side commit; never the pull request head or merge commit.","heading_ancestry":["`pull_request_target`"],"id":"semantic-occurrence:20c206e83c3f58889b9e61c33d9b1ff9fedbfd7f4f47ddac7ce4e9a0ffead31d","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_SHA to the last commit on the default branch (the pull request base branch on GHES before 3.20)","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":669,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"16854493714afa4c9a43307c6f0178ca1067431a7940fad539b4825d2f1e48b4","start_line":665},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"default types identical to pull_request.","heading_ancestry":["`pull_request_target`"],"id":"semantic-occurrence:f3b789bb871e1629e93f15f7db09586e6abb95f646808be603b4f57e0b56413d","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"by default only for opened, synchronize and reopened activity types","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target workflow"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":672,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"c75d965d62ba1f5840cdd9f2534de51a1923fa58744d2914b1e5fc5745e3896f","start_line":672},"versions":[]} +{"authority_class":"PLATFORM","conditions":["ifversion ghes < 3.20 selects the base of the pull request instead"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Prevents execution of unsafe head code; avoid when building or running pull request code.","heading_ancestry":["`pull_request_target`"],"id":"semantic-occurrence:7401611a017c13b68ab6b1ceb56a5a7344c60b6f40ab3105e0471d7a4540c61a","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"in the context of the default branch of the base repository rather than the merge commit","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Avoid when you need to build or run code from the pull request"],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":676,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"c31df378542649254e321519f4e4b1a353f02629e828af6e4848019f5bb226c3","start_line":676},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Preserved without a model rule: the patterns are not enumerated.","heading_ancestry":["`pull_request_target`"],"id":"semantic-occurrence:586530a3f463a781c6c0d0a5907493f5e2fed829ba0217735fae1c5dc3a7c13b","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"trigger","consequences":[],"exceptions":[],"modality":"MAY","object":"workflows with pull_request_target","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"branches whose names look similar to SHAs"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":678,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"664f23c23d26dc06ca1440bb15f33e397f2599881c83cc1f2e3085ebb07e62d2","start_line":678},"versions":[]} diff --git a/evidence/semantics/github-token-permissions/propositions.jsonl b/evidence/semantics/github-token-permissions/propositions.jsonl new file mode 100644 index 00000000..b15dbb33 --- /dev/null +++ b/evidence/semantics/github-token-permissions/propositions.jsonl @@ -0,0 +1,34 @@ +{"ambiguities":[],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:3fbafd1aa64e4bc7fa6be56108d290601cca18d06105a141fc8a0f1241ae4ecf","occurrence_ids":["semantic-occurrence:84e693e82c466345bd6f3bc4c1ea870d7f40519f01ab454c9163ff10876d9789"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"a unique GITHUB_TOKEN secret at the start of each workflow job","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GitHub"}} +{"ambiguities":[],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:378d760f54aa74c69a3ca546a019affc5cfb37c4d9cbc9b549ef60fbc48d1840","occurrence_ids":["semantic-occurrence:66166e598b41c65fa708a3a1ac2739f60e067b96f49d94c9b7e6e1ee8102ec52"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"be","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"a GitHub App installation access token limited to the repository that contains the workflow","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Installed when GitHub Actions is enabled"],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:1e692f20ab466d4847bb5199d509fe58a803613d53d68ed4172931ccb1d427f3","occurrence_ids":["semantic-occurrence:555cabccd7097b33829d9c917405a67290c32f72aa5c23e4b8a82adebf546272"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"expire","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"when the job finishes or after its effective maximum lifetime","parameters":[],"polarity":"POSITIVE","preconditions":["GitHub fetches an installation access token before each job begins"],"qualifiers":[],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:dbd8e10bcb0307deaa127468d61f1bae09c8e88e4392c00fdd7305641d3fc0a5","occurrence_ids":["semantic-occurrence:f7d0309d251bdbf0b7035c5d555dcc566080dab503ce1be84a5bb99c730b6d4d"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"live","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"at most 6 hours on GitHub-hosted runners","parameters":[],"polarity":"POSITIVE","preconditions":["GitHub-hosted runner"],"qualifiers":[],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:d55115079d5dababf0d8c4cf4354784e70cabff97fe120cab74f73664cf1b46a","occurrence_ids":["semantic-occurrence:f7d0309d251bdbf0b7035c5d555dcc566080dab503ce1be84a5bb99c730b6d4d"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"refresh","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"for at most 24 hours on self-hosted runners although jobs may run 5 days","parameters":[],"polarity":"POSITIVE","preconditions":["Self-hosted runner"],"qualifiers":["Use a personal access token or other authentication for jobs longer than 24 hours"],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:482dff2ed5541a705110d5f77c3e07c8ac9900e344f6267ff0f818454c8bca03","occurrence_ids":["semantic-occurrence:493494a1cbc9c3d281c61a2efa708ef7adc8970329bddebaf4e2b4f1b9cc95ac"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":["workflow_dispatch and repository_dispatch events always create workflow runs"],"modality":"DESCRIPTIVE","object":"a new workflow run","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"events triggered by the repository GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":["ifversion actions-github-token-pull-request-approval"],"authority_order":["PLATFORM"],"id":"semantic-proposition:65d8476f96920f977911a93914a55b0a9698e7d9aba96f0e0f7220151ce5bb14","occurrence_ids":["semantic-occurrence:eaa6eff3d3bebf5705c804913c97e84d48cfe0fba34501237ac405389e5f86d4"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"workflow runs in an approval-required state","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["A user with write access can start the runs by selecting Approve workflows to run","Other pull_request activity types do not create workflow runs"],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"pull_request events with opened, synchronize or reopened activity types caused by GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:3cafad63f534e6ae65484402fafc349699de2c86cc479abd6422cc62ddff2397","occurrence_ids":["semantic-occurrence:278a06109127ce63a350310b02cb8eb4c464aa1eee57fc386928e3935dd9dd2b"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"a new push-triggered workflow run","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":["Prevents accidental recursive workflow runs"],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"a push made with the repository GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:2569b1ce88484c9d3ff83b61241cf6d44a4ab2585190674f409cb7263a4c4afc","occurrence_ids":["semantic-occurrence:91d1b848d0dcc627ae4746927e7adfcaaa81af8cabcf68847e5e624efba98458"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"pass","consequences":[],"exceptions":["GITHUB_TOKEN"],"modality":"DESCRIPTIVE","object":"secrets other than GITHUB_TOKEN to the runner","parameters":[],"polarity":"NEGATIVE","preconditions":["Workflow triggered from a forked repository"],"qualifiers":[],"scope":["github/docs","data/reusables/actions/forked-secrets.md"],"subject":"GitHub Actions"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:e93d80cf9d21b9fdda383c6d7026ef8638c6d66eddac9e0f89c2da58c0f2496b","occurrence_ids":["semantic-occurrence:4901fefec53a987e9ceb443a03b3d54b79f2fbe9e5e364915d3e0cac32dd9938"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"add or remove","consequences":[],"exceptions":[],"modality":"MAY","object":"read permissions for forked repositories with the permissions key","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/forked-write-permission.md"],"subject":"workflow author"}} +{"ambiguities":["\"typically\" is not defined; the model treats the setting as the only exception"],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:d3ca3d5ae90bb60fb93e401b70198a56673d437c57330953318b3af2ff25de95","occurrence_ids":["semantic-occurrence:4901fefec53a987e9ceb443a03b3d54b79f2fbe9e5e364915d3e0cac32dd9938"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"grant","consequences":[],"exceptions":["An admin selected Send write tokens to workflows from pull requests"],"modality":"MAY","object":"write access in a forked repository with the permissions key","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["The docs say \"typically you can't\", so the normative strength is hedged"],"scope":["github/docs","data/reusables/actions/forked-write-permission.md"],"subject":"workflow author"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:0dfc87e46065ad0f2ce144183dc5bf635869e58241fb66c52c1b029906633a55","occurrence_ids":["semantic-occurrence:aa8934c774956f14647220854bef8ca3eafbea0f32d35d3260772dc4d987e0dd"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"accept","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"read, write or none as the value of each available permission","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md"],"subject":"permissions key"}} +{"ambiguities":[],"applicability":["ifversion fpt or ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:5e5fe3fc87cb12f4a4a0ff6c6c47d8e249b738480a074067a05deafe24470e68","occurrence_ids":["semantic-occurrence:3b7f29b1c0f8ae2aadd8d09186874644810cd91d67e7da0f7097cc4ea2f07b41"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"accept","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"only write or none","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md","id-token"],"subject":"id-token permission"}} +{"ambiguities":[],"applicability":["ifversion vulnerability-alerts-permission"],"authority_order":["PLATFORM"],"id":"semantic-proposition:f798880cb8cc95e85102b05f0ed7ab35bc019f80649587fe00bff611caa7d170","occurrence_ids":["semantic-occurrence:0b1622e76116d1cf40db21a51035838ce3fe6c46390f94e2b0140175258f44e9"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"accept","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"only read or none","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md","vulnerability-alerts"],"subject":"vulnerability-alerts permission"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:d0024796fb7f48632613068b825e71848572b0f2846dc215dff6fe5118e193eb","occurrence_ids":["semantic-occurrence:54a50d8f9e0387b6ff1267b679fc2574b96c4961ba57bc9983e49956d90b5c0c"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"every unspecified permission to none once any permission is specified","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md","permissions"],"subject":"GITHUB_TOKEN"}} +{"ambiguities":["The page does not say what read-all gives id-token, which has no read level; the model grants the highest allowed level not above the request, so none"],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:09a05b63d4954b23b3ccfe1864d45e09da5ee05a732b8bc6c8cc857c053a93b6","occurrence_ids":["semantic-occurrence:540e05ab0946eae9e58b59bbe7b6341a2549438d0ee0999b3bee79e984857175"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":["write-all on vulnerability-alerts is read, per the scope description reusable"],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"define","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"read or write access for all of the available permissions","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md","permissions"],"subject":"permissions: read-all or write-all"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:312ce4854d6cd28ba6b449363e8715a58adb88a1dd87e6256ff276af62a76b42","occurrence_ids":["semantic-occurrence:944f4daf9bcb74977050a42c6d58ab24581a1a76934babcdc6f6fe0355f25417"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"disable","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"permissions for all of the available permissions","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md","permissions"],"subject":"permissions: {}"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:f8760bac6957b3da2d3217b38fc055a225695219fd9e1a56af2a670a9e8aab7e","occurrence_ids":["semantic-occurrence:85720dad680ebb8d8ccdc57dfc54b4b0378bd3bedb5e5f40d95b0b951babf275"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"include","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"read access","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-scope-descriptions.md"],"subject":"write access level"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:530ccecd1c29a509244ef0bfc1ddfa87809cbb29a841378fef1a2bc6b3d88255","occurrence_ids":["semantic-occurrence:85720dad680ebb8d8ccdc57dfc54b4b0378bd3bedb5e5f40d95b0b951babf275"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"unspecified permissions to none when any is specified","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-scope-descriptions.md","permissions"],"subject":"GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":["ifversion vulnerability-alerts-permission"],"authority_order":["PLATFORM"],"id":"semantic-proposition:77b3837df07fc856b05e605c36aa261bc18052777ba583b6dec36506b357c227","occurrence_ids":["semantic-occurrence:72b4cb0ec04da2073f9acb764127af3540c8b49cac2b05a3751ddd99d82c8182"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"include","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"vulnerability-alerts as read","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-scope-descriptions.md","vulnerability-alerts"],"subject":"read-all and write-all"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:8c725812543ee0405650d431ddeeb14d5cc9cd373e495b774363e3f080ed85b2","occurrence_ids":["semantic-occurrence:daca0c42613ca7484a3ff79c18d71459406f3ad00a41dddec02adf5c46206604"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"place","consequences":[],"exceptions":[],"modality":"MAY","object":"permissions at the top level to apply to all jobs or within a specific job","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md"],"subject":"workflow author"}} +{"ambiguities":[],"applicability":["ifversion not fpt"],"authority_order":["PLATFORM"],"id":"semantic-proposition:f2aec93d0b2393385887e2981cb4eeeee4b1198e43fed9abeda358560b8f9b7d","occurrence_ids":["semantic-occurrence:a238189b1f4e33a4da816014508822e54adfeace90a94a9ccf8dd15ee3399788"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"restrict","consequences":[],"exceptions":[],"modality":"MAY","object":"write access for the GITHUB_TOKEN at the repository level","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Enterprise owners apply outside fpt"],"scope":["github/docs","data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md"],"subject":"organization owners"}} +{"ambiguities":["Whether this overrides a restricted default or only means the fork downgrade is not applied is not stated; the model implements the weaker reading"],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:c0564bcf1118561c54fd1b42a5f186759ec0f5118efe23641b180484d9b12f7c","occurrence_ids":["semantic-occurrence:de029a00d76869f72db9d2776697dcd5b116098b3c32da600d75828039809ef1"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"be granted","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"read/write repository permission when the workflow is triggered by pull_request_target, even from a public fork","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/jobs/section-assigning-permissions-to-jobs.md"],"subject":"GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:3d9812058c3b9505b5a066c508941d2eadc13de0b44e8c6a8597f4d30b998e6c","occurrence_ids":["semantic-occurrence:71424a623f0d766af0eb03a1fd752aca1f08654aec7de890311ded4837ad4f0a"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"calculate","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"job token permissions starting from the enterprise, organization or repository default, then workflow level, then job level","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/workflow-syntax.md"],"subject":"GitHub"}} +{"ambiguities":["A write permission on a scope with no read level (id-token) has no read to fall to; the model sets it to none"],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:3824bf4fabf179d855589fda08543f63c18837de4955d826b75da21b864cc309","occurrence_ids":["semantic-occurrence:71424a623f0d766af0eb03a1fd752aca1f08654aec7de890311ded4837ad4f0a"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"change","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"every write permission to read only after calculation","parameters":[],"polarity":"POSITIVE","preconditions":["Workflow triggered by a pull request event other than pull_request_target from a forked repository","Send write tokens to workflows from pull requests is not selected"],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/workflow-syntax.md"],"subject":"GitHub"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:3dba592ed0b4100d0fccac9aa4d8791ae9f148ed9311fda8fe8062cdd760f885","occurrence_ids":["semantic-occurrence:a8161c3133bb0e9792816592a72c218e00b9071c3bbe654c755e9449ffde1230"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_SHA to the last merge commit on the GITHUB_REF branch and GITHUB_REF to refs/pull/PULL_REQUEST_NUMBER/merge","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Consumers needing the head commit must read it from the event payload"],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request event"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:4fd2b55acf794aa6b999bb2faeb548c07421e46d24ea375cb7985a221a806a79","occurrence_ids":["semantic-occurrence:5ad49b8e8364302cebec7bf9a5586233002fad28b303cff241e7551fa65d72bc"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"by default only for opened, synchronize and reopened activity types","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request workflow"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:ba128e2185b0141dfece458a52d7b71e3c9041cc63b7f8d2c7220bb947fa5708","occurrence_ids":["semantic-occurrence:8751bea4bc24f0a3900ef001b9383ce95908c5e5faa693b8619319b7160b8bfe"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":["pull_request_target runs even with a merge conflict"],"modality":"DESCRIPTIVE","object":"on a pull request that has a merge conflict","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request workflow"}} +{"ambiguities":["Contradicts common use of github.event.pull_request fields in same-repository runs and is not modelled; verify against a real run before relying on it"],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:b465df3f413413fe2c5ded1f5e2d7cbd368499fb59d48d1f3492577ab767ab2e","occurrence_ids":["semantic-occurrence:bcd282fc5bb4f2faa54a923fb98a13162db65c3329793d248452f490e0d7dc10"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"be","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"empty for merged pull requests and pull requests from forked repositories","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request webhook event payload"}} +{"ambiguities":[],"applicability":["ifversion actions-github-token-pull-request-approval"],"authority_order":["PLATFORM"],"id":"semantic-proposition:14f0e55241cee49c2b792b3f8da2153794a7ed7c1b0afdf2084dc74437d6d0de","occurrence_ids":["semantic-occurrence:e686dc1efbb0bdb1235517b83b805ef3448366f10e5bae0ccee3f63c5f509a1f"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"workflow runs that require approval for opened, synchronize and reopened","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request events caused by a workflow using GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":["ifversion ghes < 3.20"],"authority_order":["PLATFORM"],"id":"semantic-proposition:445765dca582770c0ddf33c6c7083c750962ef66faf7f836217fb1f14af9b6cd","occurrence_ids":["semantic-occurrence:20c206e83c3f58889b9e61c33d9b1ff9fedbfd7f4f47ddac7ce4e9a0ffead31d"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_SHA to the last commit on the default branch (the pull request base branch on GHES before 3.20)","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:7e9d2d42a5cd7ff280534613bc878f777bfa901d88a1ca3c44922c3136e10b7b","occurrence_ids":["semantic-occurrence:f3b789bb871e1629e93f15f7db09586e6abb95f646808be603b4f57e0b56413d"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"by default only for opened, synchronize and reopened activity types","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target workflow"}} +{"ambiguities":[],"applicability":["ifversion ghes < 3.20 selects the base of the pull request instead"],"authority_order":["PLATFORM"],"id":"semantic-proposition:e2c059f42e2630791d78203a5f5f84c79401329be349b3e5c71cfc07854e10da","occurrence_ids":["semantic-occurrence:7401611a017c13b68ab6b1ceb56a5a7344c60b6f40ab3105e0471d7a4540c61a"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"in the context of the default branch of the base repository rather than the merge commit","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Avoid when you need to build or run code from the pull request"],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"}} +{"ambiguities":["Patterns are undefined in the source; no behaviour can be derived"],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:4a7fafbddd6c3dd85d900bea829f8ea3e4a88acb3d54f70f1e28a3a27d7848b5","occurrence_ids":["semantic-occurrence:586530a3f463a781c6c0d0a5907493f5e2fed829ba0217735fae1c5dc3a7c13b"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"trigger","consequences":[],"exceptions":[],"modality":"MAY","object":"workflows with pull_request_target","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"branches whose names look similar to SHAs"}} diff --git a/ges/__main__.py b/ges/__main__.py index 5f6a6a78..a3f54189 100644 --- a/ges/__main__.py +++ b/ges/__main__.py @@ -17,6 +17,8 @@ def main(argv=None): configure_parser(sub) from .receipts import configure_parser as configure_receipts configure_receipts(sub) + from .token_semantics import configure_parser as configure_token_semantics + configure_token_semantics(sub) c=sub.add_parser('branch-checks', help='Read-only exact-revision required-check observation; no enforcement certification') c.add_argument('--snapshot',type=Path,required=True); c.add_argument('--profile',type=Path,required=True) c.add_argument('--revision',required=True); c.add_argument('--revision-kind',choices=['head','test_merge','merge_group'],required=True) @@ -46,6 +48,9 @@ def main(argv=None): report=assess(load(args.snapshot),load(args.profile),args.revision,args.revision_kind) dump(args.output,report); print(json.dumps(report['summary'],indent=2)) return 0 if report['observed_checks_pass'] else 1 + if args.command == 'token-semantics': + from .token_semantics import run as run_token_semantics + return run_token_semantics(args) if args.command == 'receipt': from .receipts import run as run_receipt return run_receipt(args) diff --git a/ges/token_semantics.py b/ges/token_semantics.py new file mode 100644 index 00000000..a5d10197 --- /dev/null +++ b/ges/token_semantics.py @@ -0,0 +1,428 @@ +"""GITHUB_TOKEN permission and pull-request event semantics from pinned GitHub Docs. + +Two halves that must agree: + +* an executable model (`resolve_token_permissions`, `pull_request_event_context`, + `workflow_run_for_token_event`) whose every rule has an id (`T*` for token, + `E*` for event semantics); +* a source-bound ledger (`evidence/semantics/github-token-permissions/`) of + `ges.semantic-occurrence.v1` / `ges.semantic-proposition.v1` records, each bound to an + exact line span of a file at the pinned `github/docs` commit, naming the rules it + supports. + +`build_ledger` compiles authored annotations against the pinned source bytes and fails +on any drift (content digest, anchor text, line range). `check_conformance` proves the +model reproduces every case in `conformance.json`, that every rule has a case and a +supporting source span, and that no case cites an unknown rule. + +Everything is PROPOSED: the records carry no reviewer. Structural validity and model +conformance do not certify that the documentation was interpreted correctly, and the +documentation is not a specification of GitHub's server behaviour. +Standard library only. +""" +from __future__ import annotations + +import argparse +import hashlib +import json +import sys +from pathlib import Path + +from .semantics import canonical_bytes, stable_id, validate_record + +ROOT = Path(__file__).resolve().parents[1] +LEDGER_DIR = ROOT / 'evidence' / 'semantics' / 'github-token-permissions' +ANNOTATIONS = LEDGER_DIR / 'annotations.json' +CONFORMANCE = LEDGER_DIR / 'conformance.json' +OCCURRENCES = LEDGER_DIR / 'occurrences.jsonl' +PROPOSITIONS = LEDGER_DIR / 'propositions.jsonl' +ACCOUNTING = LEDGER_DIR / 'accounting.json' +ANNOTATIONS_SCHEMA = 'ges.token-semantics-annotations.v1' +CONFORMANCE_SCHEMA = 'ges.token-semantics-conformance.v1' + +LEVELS = ('none', 'read', 'write') + +# Scope -> (levels the docs allow, version flag that gates it or None). +# Source: data/reusables/actions/github-token-available-permissions.md lines 3-22 and +# github-token-scope-descriptions.md. `id-token` allows write|none and only on fpt/ghec; +# `vulnerability-alerts` allows read|none. Flags are the docs' own `ifversion` names. +SCOPES: dict[str, tuple[tuple[str, ...], str | None]] = { + 'actions': (('none', 'read', 'write'), None), + 'artifact-metadata': (('none', 'read', 'write'), 'artifact-metadata'), + 'attestations': (('none', 'read', 'write'), 'artifact-attestations'), + 'checks': (('none', 'read', 'write'), None), + 'code-quality': (('none', 'read', 'write'), 'code-quality'), + 'contents': (('none', 'read', 'write'), None), + 'deployments': (('none', 'read', 'write'), None), + 'discussions': (('none', 'read', 'write'), None), + 'id-token': (('none', 'write'), 'fpt-or-ghec'), + 'issues': (('none', 'read', 'write'), None), + 'packages': (('none', 'read', 'write'), None), + 'pages': (('none', 'read', 'write'), None), + 'pull-requests': (('none', 'read', 'write'), None), + 'security-events': (('none', 'read', 'write'), None), + 'statuses': (('none', 'read', 'write'), None), + 'vulnerability-alerts': (('none', 'read'), 'vulnerability-alerts-permission'), +} + +# Rule T8: events the docs call "pull request events" (event headings in +# events-that-trigger-workflows.md other than the deprecated pull_request_comment alias +# and pull_request_target, which the calculation exempts by name). +FORK_DOWNGRADED_EVENTS = frozenset({'pull_request', 'pull_request_review', + 'pull_request_review_comment'}) +PULL_REQUEST_TARGET = 'pull_request_target' +DEFAULT_PULL_REQUEST_ACTIVITY_TYPES = ('opened', 'synchronize', 'reopened') + + +def available_scopes(flags: frozenset[str] = frozenset()) -> dict[str, tuple[str, ...]]: + """Scopes (and their allowed levels) available given the docs' version flags.""" + return {name: levels for name, (levels, flag) in SCOPES.items() + if flag is None or flag in flags} + + +def _grant(allowed: tuple[str, ...], requested: str) -> str: + """Highest allowed level not above `requested` (read-all on a write|none scope is none).""" + best = 'none' + for level in allowed: + if LEVELS.index(level) <= LEVELS.index(requested) and LEVELS.index(level) >= LEVELS.index(best): + best = level + return best + + +def apply_permissions_key(spec: object, flags: frozenset[str] = frozenset()) -> dict[str, str]: + """Rules T4-T7: the access map a `permissions` value denotes, independent of defaults. + + `spec` is a mapping scope -> level, `'read-all'`, `'write-all'` or `{}`. + Raises ValueError for unknown scopes or levels the docs do not allow. + """ + scopes = available_scopes(flags) + if spec == 'read-all': + return {name: _grant(levels, 'read') for name, levels in scopes.items()} + if spec == 'write-all': + return {name: _grant(levels, 'write') for name, levels in scopes.items()} + if not isinstance(spec, dict): + raise ValueError('permissions must be a mapping, read-all or write-all') + result = {name: 'none' for name in scopes} + for name, level in spec.items(): + if name not in scopes: + raise ValueError(f'unknown or unavailable permission scope: {name}') + if level not in scopes[name]: + raise ValueError(f'scope {name} cannot be set to {level!r} (allowed: {"|".join(scopes[name])})') + result[name] = level + return result + + +def resolve_token_permissions(*, default: dict[str, str], workflow: object = None, job: object = None, + event: str, from_fork: bool, send_write_tokens: bool = False, + flags: frozenset[str] = frozenset()) -> dict[str, str]: + """Rule T8: default -> workflow `permissions` -> job `permissions` -> fork adjustment. + + `default` is the enterprise/organization/repository default access map, supplied by the + caller: the pinned pages do not enumerate the restricted/permissive presets. + `workflow`/`job` are `permissions` values or None when the key is absent. + """ + scopes = available_scopes(flags) + for name, level in default.items(): + if name not in scopes or level not in scopes[name]: + raise ValueError(f'invalid default permission {name}: {level}') + current = {name: default.get(name, 'none') for name in scopes} + if workflow is not None: + current = apply_permissions_key(workflow, flags) + if job is not None: + current = apply_permissions_key(job, flags) + if from_fork and event in FORK_DOWNGRADED_EVENTS and not send_write_tokens: + current = {name: ('read' if level == 'write' and 'read' in scopes[name] else + 'none' if level == 'write' else level) + for name, level in current.items()} + return current + + +def allows(permissions: dict[str, str], scope: str, needed: str) -> bool: + """Rule T4: `write` includes `read`.""" + return LEVELS.index(permissions.get(scope, 'none')) >= LEVELS.index(needed) + + +def pull_request_event_context(event: str, *, ghes_before_3_20: bool = False) -> dict[str, object]: + """Rules E1, E2, E4: what GITHUB_SHA and GITHUB_REF denote for the two PR triggers.""" + if event == 'pull_request': + return {'sha': 'last merge commit on the GITHUB_REF branch', + 'ref': 'refs/pull/PULL_REQUEST_NUMBER/merge', + 'runs_against': 'merge commit of the pull request', + 'default_activity_types': DEFAULT_PULL_REQUEST_ACTIVITY_TYPES, + 'skipped_on_merge_conflict': True} + if event == PULL_REQUEST_TARGET: + return {'sha': 'last commit on the pull request base branch' if ghes_before_3_20 + else 'last commit on default branch', + 'ref': 'pull request base branch' if ghes_before_3_20 else 'default branch', + 'runs_against': 'base of the pull request' if ghes_before_3_20 + else 'default branch of the base repository', + 'default_activity_types': DEFAULT_PULL_REQUEST_ACTIVITY_TYPES, + 'skipped_on_merge_conflict': False} + raise ValueError(f'not a modelled pull request trigger: {event}') + + +def workflow_run_for_token_event(event: str, activity_type: str | None = None, *, + approval_flag: bool = True) -> str: + """Rule E3: does an event caused by the repository GITHUB_TOKEN create a workflow run? + + Returns RUN, APPROVAL_REQUIRED or NO_RUN. `approval_flag` is the docs' + `actions-github-token-pull-request-approval` version flag. + """ + if event in ('workflow_dispatch', 'repository_dispatch'): + return 'RUN' + if (approval_flag and event == 'pull_request' + and activity_type in DEFAULT_PULL_REQUEST_ACTIVITY_TYPES): + return 'APPROVAL_REQUIRED' + return 'NO_RUN' + + +def token_is_valid(runner: str, elapsed_hours: float, *, job_finished: bool) -> bool: + """Rule T3: valid until the job finishes or the runner type's effective maximum lifetime. + + `github-hosted`: 6 hours (maximum job execution time). `self-hosted`: jobs may run + 5 days but an installation token can be refreshed for at most 24 hours. + """ + limits = {'github-hosted': 6.0, 'self-hosted': 24.0} + if runner not in limits: + raise ValueError(f'unknown runner type: {runner}') + return not job_finished and elapsed_hours < limits[runner] + + +def secrets_reach_runner(name: str, *, from_fork: bool) -> bool: + """Rule T13: from a forked repository only GITHUB_TOKEN reaches the runner.""" + return name == 'GITHUB_TOKEN' or not from_fork + + +# --- source-bound ledger --------------------------------------------------------------- + +def sha(raw: bytes) -> str: + return hashlib.sha256(raw).hexdigest() + + +def _require(condition: bool, message: str) -> None: + if not condition: + raise ValueError(message) + + +def _load(path: Path) -> dict: + return json.loads(path.read_text(encoding='utf-8')) + + +def _annotations() -> dict: + data = _load(ANNOTATIONS) + _require(data.get('schema') == ANNOTATIONS_SCHEMA, 'Unsupported annotations schema') + return data + + +def build_ledger(annotations: dict, sources: Path) -> dict[str, bytes]: + """Compile occurrences, propositions and accounting from annotations and pinned bytes.""" + repository, commit = annotations['repository'], annotations['commit'] + occurrences, propositions = [], [] + modelled: set[str] = set() + contents: dict[str, tuple[str, list[bytes]]] = {} + for entry in annotations['entries']: + path = entry['path'] + _require(not path.startswith('/') and '..' not in path.split('/') and '\\' not in path, + 'Unsafe source path') + if path not in contents: + raw = (sources / path).read_bytes() + contents[path] = (sha(raw), raw.splitlines(keepends=True)) + content_digest, lines = contents[path] + start, end = entry['start_line'], entry['end_line'] + _require(1 <= start <= end <= len(lines), f'{path}: span {start}-{end} outside file') + span = b''.join(lines[start - 1:end]) + _require(entry['expect'] in span.decode('utf-8'), + f'{path}:{start}-{end}: anchor text not found in span (source drift)') + _require(entry['content_sha256'] == content_digest, + f'{path}: pinned content digest differs from annotation') + occurrence = { + 'schema': 'ges.semantic-occurrence.v1', + 'source': {'repository': repository, 'commit': commit, 'path': path, + 'content_sha256': content_digest, 'start_line': start, 'end_line': end, + 'span_sha256': sha(span)}, + 'heading_ancestry': entry['heading_ancestry'], + 'include_chain': entry['include_chain'], + 'page_applications': [], + 'conditions': entry['conditions'], + 'products': ['GitHub Actions'], + 'versions': entry['versions'], + 'authority_class': 'PLATFORM', + 'semantic_kind': entry['semantic_kind'], + 'semantic_ast': entry['propositions'][0]['semantic_ast'], + 'formalization': ' '.join(p['formalization'] for p in entry['propositions']), + 'extraction': {'method': 'authored-span-annotations', 'version': 'token-semantics.v1'}, + 'review': {'status': 'PROPOSED', 'reviewer': None, 'evidence_reference': None}, + } + occurrence['id'] = stable_id(occurrence) + validate_record(occurrence) + occurrences.append(occurrence) + for item in entry['propositions']: + proposition = { + 'schema': 'ges.semantic-proposition.v1', + 'semantic_ast': item['semantic_ast'], + 'occurrence_ids': [occurrence['id']], + 'preserved_differences': item.get('preserved_differences', []), + 'authority_order': ['PLATFORM'], + 'applicability': entry['conditions'] + entry['versions'], + 'ambiguities': item.get('ambiguities', []), + 'primary_review': {'status': 'PROPOSED', 'reviewer': None, 'evidence_reference': None}, + 'omission_review': {'status': 'PROPOSED', 'reviewer': None, 'evidence_reference': None}, + } + proposition['id'] = stable_id(proposition) + validate_record(proposition) + propositions.append(proposition) + modelled.update(item.get('rules', [])) + _require(len({p['id'] for p in propositions}) == len(propositions), + 'Two annotated statements have an identical semantic AST') + accounting = { + 'schema': 'ges.token-semantics-accounting.v1', + 'repository': repository, + 'commit': commit, + 'files': len(contents), + 'occurrences': len(occurrences), + 'propositions': len(propositions), + 'propositions_with_model_rule': sum( + 1 for entry in annotations['entries'] for p in entry['propositions'] if p.get('rules')), + 'propositions_preserved_without_model_rule': sum( + 1 for entry in annotations['entries'] for p in entry['propositions'] if not p.get('rules')), + 'rules_supported': sorted(modelled), + 'review_status': 'PROPOSED', + 'semantic_truth_certified': False, + 'policy_adopted': False, + 'scope': ('Selected GITHUB_TOKEN permission and pull-request trigger passages only; ' + 'not a review of the GitHub Docs corpus.'), + } + + def jsonl(records: list[dict]) -> bytes: + return b''.join(canonical_bytes(r) + b'\n' for r in records) + + return {'occurrences.jsonl': jsonl(occurrences), 'propositions.jsonl': jsonl(propositions), + 'accounting.json': json.dumps(accounting, indent=2, sort_keys=True).encode('utf-8') + b'\n'} + + +def check_conformance(annotations: dict, conformance: dict) -> list[str]: + """Run every conformance case through the model; return all problems found.""" + problems: list[str] = [] + supported = {rule for entry in annotations['entries'] + for p in entry['propositions'] for rule in p.get('rules', [])} + if conformance.get('schema') != CONFORMANCE_SCHEMA: + return ['conformance: unsupported schema'] + covered: set[str] = set() + for case in conformance['cases']: + name = case['id'] + rules = set(case['rules']) + covered |= rules + for rule in sorted(rules - supported): + problems.append(f'{name}: cites rule {rule} that no annotated source statement supports') + actual = run_case(case) + if actual != case['expect']: + problems.append(f'{name}: expected {case["expect"]!r}, model produced {actual!r}') + for rule in sorted(supported - covered): + problems.append(f'rule {rule} is supported by source statements but has no conformance case') + return problems + + +def run_case(case: dict) -> dict: + """Run one conformance case; a ValueError from the model is the observable result.""" + try: + return _run_case(case) + except ValueError as exc: + return {'error': str(exc)} + + +def _run_case(case: dict) -> dict: + kind, given = case['kind'], case['given'] + flags = frozenset(given.get('flags', [])) + if kind == 'permissions-key': + return {'permissions': apply_permissions_key(given['spec'], flags)} + if kind == 'resolve': + return {'permissions': resolve_token_permissions( + default=given['default'], workflow=given.get('workflow'), job=given.get('job'), + event=given['event'], from_fork=given['from_fork'], + send_write_tokens=given.get('send_write_tokens', False), flags=flags)} + if kind == 'can': + permissions = resolve_token_permissions( + default=given['default'], workflow=given.get('workflow'), job=given.get('job'), + event=given['event'], from_fork=given['from_fork'], + send_write_tokens=given.get('send_write_tokens', False), flags=flags) + return {'allowed': allows(permissions, given['scope'], given['needed'])} + if kind == 'event-context': + context = pull_request_event_context(given['event'], ghes_before_3_20=given.get('ghes_before_3_20', False)) + return {'context': {k: list(v) if isinstance(v, tuple) else v for k, v in context.items()}} + if kind == 'token-event': + return {'result': workflow_run_for_token_event( + given['event'], given.get('activity_type'), approval_flag=given.get('approval_flag', True))} + if kind == 'token-valid': + return {'valid': token_is_valid(given['runner'], given['elapsed_hours'], job_finished=given['job_finished'])} + if kind == 'secret': + return {'reaches_runner': secrets_reach_runner(given['name'], from_fork=given['from_fork'])} + raise ValueError(f'unknown case kind {kind}') + + +def verify_sources(sources: Path) -> list[str]: + """Rebuild the ledger from pinned source bytes and compare with the committed files.""" + problems = [] + try: + built = build_ledger(_annotations(), sources) + except (OSError, ValueError, KeyError) as exc: + return [str(exc)] + for name, raw in built.items(): + if (LEDGER_DIR / name).read_bytes() != raw: + problems.append(f'{name}: differs from a rebuild against the pinned sources') + return problems + + +def configure_parser(subparsers) -> None: + parser = subparsers.add_parser('token-semantics', help=__doc__.splitlines()[0]) + commands = parser.add_subparsers(dest='token_command', required=True) + command = commands.add_parser('build', help='Compile the ledger from pinned github/docs files') + command.add_argument('--sources', type=Path, required=True, + help='Directory laid out like the github/docs checkout at the pinned commit') + command.add_argument('--output', type=Path, default=LEDGER_DIR) + command = commands.add_parser('verify', help='Check the committed ledger against pinned sources') + command.add_argument('--sources', type=Path, required=True) + commands.add_parser('conformance', help='Check the executable model against conformance cases') + commands.add_parser('validate', help='Validate committed record structure and identity') + + +def run(args) -> int: + try: + if args.token_command == 'build': + built = build_ledger(_annotations(), args.sources) + args.output.mkdir(parents=True, exist_ok=True) + for name, raw in built.items(): + (args.output / name).write_bytes(raw) + print(json.dumps({'written': sorted(built), 'review_status': 'PROPOSED'})) + return 0 + if args.token_command == 'verify': + problems = verify_sources(args.sources) + elif args.token_command == 'conformance': + problems = check_conformance(_annotations(), _load(CONFORMANCE)) + else: + problems = [] + for name in ('occurrences.jsonl', 'propositions.jsonl'): + for number, line in enumerate((LEDGER_DIR / name).read_text(encoding='utf-8').splitlines(), 1): + try: + validate_record(json.loads(line)) + except (ValueError, KeyError) as exc: + problems.append(f'{name}:{number}: {exc}') + for problem in problems: + print(problem, file=sys.stderr) + if not problems: + print(json.dumps({'ok': True, 'command': args.token_command, + 'semantic_truth_certified': False})) + return 2 if problems else 0 + except (OSError, ValueError, KeyError) as exc: + print('token-semantics: ' + str(exc), file=sys.stderr) + return 2 + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + sub = parser.add_subparsers(dest='command', required=True) + configure_parser(sub) + return run(parser.parse_args(sys.argv[1:] if argv is None else argv)) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tests/test_token_semantics.py b/tests/test_token_semantics.py new file mode 100644 index 00000000..4a697b6a --- /dev/null +++ b/tests/test_token_semantics.py @@ -0,0 +1,216 @@ +import copy +import hashlib +import json +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + +import yaml + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +from ges import receipts, token_semantics as ts # noqa: E402 +from ges.semantics import validate_record # noqa: E402 + +CONFORMANCE = json.loads(ts.CONFORMANCE.read_text(encoding='utf-8')) +ANNOTATIONS = json.loads(ts.ANNOTATIONS.read_text(encoding='utf-8')) +RESTRICTED = {'contents': 'read', 'packages': 'read'} + + +class Conformance(unittest.TestCase): + def test_committed_cases_all_pass_and_cover_every_supported_rule(self): + self.assertEqual(ts.check_conformance(ANNOTATIONS, CONFORMANCE), []) + + def test_changed_expectation_is_reported(self): + broken = copy.deepcopy(CONFORMANCE) + broken['cases'][0]['expect'] = {'allowed': not broken['cases'][0]['expect']['allowed']} + problems = ts.check_conformance(ANNOTATIONS, broken) + self.assertEqual(len(problems), 1) + self.assertIn(broken['cases'][0]['id'], problems[0]) + + def test_uncited_source_rule_and_unsupported_cited_rule_are_reported(self): + trimmed = copy.deepcopy(CONFORMANCE) + trimmed['cases'] = [c for c in trimmed['cases'] if 'T12' not in c['rules']] + self.assertTrue(any('rule T12' in p for p in ts.check_conformance(ANNOTATIONS, trimmed))) + extra = copy.deepcopy(CONFORMANCE) + extra['cases'][0]['rules'] = ['T99'] + self.assertTrue(any('T99' in p for p in ts.check_conformance(ANNOTATIONS, extra))) + + def test_schema_is_checked(self): + self.assertEqual(ts.check_conformance(ANNOTATIONS, {'schema': 'x', 'cases': []}), + ['conformance: unsupported schema']) + + +class Model(unittest.TestCase): + def resolve(self, **kwargs): + defaults = {'default': RESTRICTED, 'event': 'push', 'from_fork': False} + return ts.resolve_token_permissions(**{**defaults, **kwargs}) + + def test_fork_downgrade_applies_only_to_pull_request_events_not_target(self): + for event in ('pull_request', 'pull_request_review', 'pull_request_review_comment'): + self.assertEqual(self.resolve(event=event, from_fork=True, workflow={'issues': 'write'})['issues'], 'read') + self.assertEqual(self.resolve(event='pull_request_target', from_fork=True, + workflow={'issues': 'write'})['issues'], 'write') + + def test_job_key_replaces_not_merges_with_workflow_key(self): + result = self.resolve(workflow={'contents': 'write', 'issues': 'write'}, job={'checks': 'read'}) + self.assertEqual({k: v for k, v in result.items() if v != 'none'}, {'checks': 'read'}) + + def test_unknown_scope_and_bad_default_raise(self): + with self.assertRaises(ValueError): + ts.apply_permissions_key({'contentz': 'read'}) + with self.assertRaises(ValueError): + ts.apply_permissions_key('admin-all') + with self.assertRaises(ValueError): + self.resolve(default={'contents': 'admin'}) + + def test_read_all_is_clipped_to_scope_levels(self): + flags = frozenset({'fpt-or-ghec', 'vulnerability-alerts-permission'}) + read_all = ts.apply_permissions_key('read-all', flags) + self.assertEqual(read_all['id-token'], 'none') + self.assertEqual(read_all['vulnerability-alerts'], 'read') + self.assertEqual(ts.apply_permissions_key('write-all', flags)['vulnerability-alerts'], 'read') + + def test_unknown_runner_rejected(self): + with self.assertRaises(ValueError): + ts.token_is_valid('hosted', 1.0, job_finished=False) + + +class ReceiptActionConsequences(unittest.TestCase): + """The receipt action's documented limits follow from the pinned documentation.""" + + def test_fork_pull_request_cannot_write_the_pull_request_body(self): + caller = {'pull-requests': 'write', 'contents': 'read'} # the caller in docs/agent-verification-receipts.md + same_repo = ts.resolve_token_permissions(default=RESTRICTED, workflow=caller, + event='pull_request', from_fork=False) + fork = ts.resolve_token_permissions(default=RESTRICTED, workflow=caller, + event='pull_request', from_fork=True) + self.assertTrue(ts.allows(same_repo, 'pull-requests', 'write')) + self.assertFalse(ts.allows(fork, 'pull-requests', 'write')) + self.assertTrue(ts.allows(fork, 'pull-requests', 'read')) + + def test_documented_caller_permissions_match_the_modelled_requirement(self): + text = (ROOT / 'docs' / 'agent-verification-receipts.md').read_text(encoding='utf-8') + block = text.split('```yaml', 1)[1].split('```', 1)[0] + permissions = yaml.safe_load(block)['permissions'] + resolved = ts.resolve_token_permissions(default=RESTRICTED, workflow=permissions, + event='pull_request', from_fork=False) + self.assertTrue(ts.allows(resolved, 'pull-requests', 'write')) + + def test_action_refuses_the_event_whose_token_stays_writable_for_forks(self): + action = yaml.safe_load((ROOT / '.github' / 'actions' / 'inject-agent-receipt' / 'action.yml') + .read_text(encoding='utf-8')) + script = action['runs']['steps'][0]['run'] + self.assertIn(ts.PULL_REQUEST_TARGET, script) + target = ts.resolve_token_permissions(default=RESTRICTED, workflow={'pull-requests': 'write'}, + event=ts.PULL_REQUEST_TARGET, from_fork=True) + self.assertTrue(ts.allows(target, 'pull-requests', 'write')) + + def test_head_sha_binding_needs_the_payload_because_github_sha_is_the_merge_commit(self): + self.assertIn('merge commit', ts.pull_request_event_context('pull_request')['sha']) + + def test_token_edit_of_the_body_creates_no_workflow_run(self): + self.assertEqual(ts.workflow_run_for_token_event('pull_request', 'edited'), 'NO_RUN') + + +class LedgerBuild(unittest.TestCase): + def committed(self): + return [json.loads(line) for name in ('occurrences.jsonl', 'propositions.jsonl') + for line in (ts.LEDGER_DIR / name).read_text(encoding='utf-8').splitlines()] + + def test_committed_records_are_valid_proposed_and_account_for_annotations(self): + records = self.committed() + for record in records: + validate_record(record) + occurrences = [r for r in records if r['schema'] == 'ges.semantic-occurrence.v1'] + propositions = [r for r in records if r['schema'] == 'ges.semantic-proposition.v1'] + self.assertEqual(len(occurrences), len(ANNOTATIONS['entries'])) + self.assertEqual(len(propositions), sum(len(e['propositions']) for e in ANNOTATIONS['entries'])) + for record in occurrences: + self.assertEqual(record['review'], {'status': 'PROPOSED', 'reviewer': None, 'evidence_reference': None}) + self.assertEqual(record['source']['commit'], ANNOTATIONS['commit']) + for record in propositions: + self.assertEqual(record['primary_review']['status'], 'PROPOSED') + self.assertEqual(record['omission_review']['status'], 'PROPOSED') + accounting = json.loads(ts.ACCOUNTING.read_text(encoding='utf-8')) + self.assertFalse(accounting['semantic_truth_certified']) + self.assertFalse(accounting['policy_adopted']) + self.assertEqual(accounting['occurrences'], len(occurrences)) + self.assertEqual(accounting['propositions'], len(propositions)) + + def synthetic(self, text='alpha\nbeta rule here\ngamma\n'): + directory = tempfile.TemporaryDirectory() + self.addCleanup(directory.cleanup) + (Path(directory.name) / 'a.md').write_bytes(text.encode()) + annotations = { + 'schema': ts.ANNOTATIONS_SCHEMA, 'repository': 'github/docs', 'commit': 'a' * 40, + 'entries': [{ + 'path': 'a.md', 'start_line': 2, 'end_line': 2, 'expect': 'rule here', + 'content_sha256': hashlib.sha256('alpha\nbeta rule here\ngamma\n'.encode()).hexdigest(), + 'heading_ancestry': [], 'include_chain': [], 'conditions': [], 'versions': [], + 'semantic_kind': 'FACT', + 'propositions': [{'semantic_ast': { + 'subject': 's', 'action': 'a', 'object': 'o', 'modality': 'DESCRIPTIVE', 'polarity': 'POSITIVE', + 'scope': [], 'preconditions': [], 'qualifiers': [], 'exceptions': [], 'consequences': [], + 'parameters': []}, 'formalization': 'f', 'rules': ['T4']}]}]} + return Path(directory.name), annotations + + def test_synthetic_build_binds_exact_span_bytes(self): + sources, annotations = self.synthetic() + built = ts.build_ledger(annotations, sources) + occurrence = json.loads(built['occurrences.jsonl']) + self.assertEqual(occurrence['source']['span_sha256'], hashlib.sha256(b'beta rule here\n').hexdigest()) + self.assertEqual(built, ts.build_ledger(annotations, sources), 'build must be deterministic') + + def test_source_drift_is_rejected(self): + sources, annotations = self.synthetic() + (sources / 'a.md').write_bytes(b'alpha\nbeta rule here\ngamma changed\n') + with self.assertRaisesRegex(ValueError, 'content digest differs'): + ts.build_ledger(annotations, sources) + + def test_anchor_and_range_drift_are_rejected(self): + sources, annotations = self.synthetic() + moved = copy.deepcopy(annotations) + moved['entries'][0]['start_line'] = moved['entries'][0]['end_line'] = 3 + with self.assertRaisesRegex(ValueError, 'anchor text not found'): + ts.build_ledger(moved, sources) + outside = copy.deepcopy(annotations) + outside['entries'][0]['end_line'] = 9 + with self.assertRaisesRegex(ValueError, 'outside file'): + ts.build_ledger(outside, sources) + + def test_unsafe_path_and_duplicate_statement_are_rejected(self): + sources, annotations = self.synthetic() + unsafe = copy.deepcopy(annotations) + unsafe['entries'][0]['path'] = '../a.md' + with self.assertRaisesRegex(ValueError, 'Unsafe source path'): + ts.build_ledger(unsafe, sources) + duplicate = copy.deepcopy(annotations) + duplicate['entries'].append(copy.deepcopy(duplicate['entries'][0])) + with self.assertRaisesRegex(ValueError, 'identical semantic AST'): + ts.build_ledger(duplicate, sources) + + def test_ledger_carries_no_template_markup(self): + # Statements are formalized ASTs bound to the source by hashes, not copied template text. + for record in self.committed(): + self.assertNotIn('{%', json.dumps(record)) + + +class Cli(unittest.TestCase): + def test_conformance_and_validate_commands_exit_zero(self): + for command in ('conformance', 'validate'): + result = subprocess.run([sys.executable, '-m', 'ges', 'token-semantics', command], + cwd=ROOT, capture_output=True, text=True) + self.assertEqual(result.returncode, 0, result.stderr) + + def test_verify_with_missing_sources_fails_visibly(self): + with tempfile.TemporaryDirectory() as empty: + result = subprocess.run([sys.executable, '-m', 'ges', 'token-semantics', 'verify', '--sources', empty], + cwd=ROOT, capture_output=True, text=True) + self.assertEqual(result.returncode, 2) + + +if __name__ == '__main__': + unittest.main() From 35c9954575e4a8f9c4a4bb46102d0a1a42de8890 Mon Sep 17 00:00:00 2001 From: 4444jPPP Date: Sat, 14 Nov 2026 09:19:29 -0500 Subject: [PATCH 2/2] fix: address review findings on token semantics (fork secrets by trigger, merged-closure ref, version applicability) Co-Authored-By: Claude Sonnet 5.5 --- docs/github-token-permission-semantics.md | 8 +- .../github-token-permissions/accounting.json | 8 +- .../github-token-permissions/annotations.json | 98 ++++++++++++++++++- .../github-token-permissions/conformance.json | 39 ++++++++ .../occurrences.jsonl | 8 +- .../propositions.jsonl | 8 +- ges/token_semantics.py | 27 +++-- tests/test_token_semantics.py | 17 ++++ 8 files changed, 187 insertions(+), 26 deletions(-) diff --git a/docs/github-token-permission-semantics.md b/docs/github-token-permission-semantics.md index 8648827f..1fe330d4 100644 --- a/docs/github-token-permission-semantics.md +++ b/docs/github-token-permission-semantics.md @@ -21,7 +21,7 @@ adopted, no control accepted, and no live GitHub behaviour was observed. Where t prose is silent or hedged the model says so (see Ambiguities) rather than guessing. Source: `github/docs` at `56fcfa816f27bca239e5d39fff0d4f74f77ec995` (the commit in -`sources/sources.lock.json`), nine files, 30 occurrences, 34 propositions. 30 +`sources/sources.lock.json`), ten files, 32 occurrences, 36 propositions. 32 propositions name a model rule; 4 are preserved without one. The ledger stores hashes and formalized ASTs, not the prose. @@ -44,8 +44,8 @@ from the docs' permission list; `id-token` allows `write|none` (fpt/ghec), | T10 | In a fork run the key can add or remove `read` but typically not grant `write` | `resolve_token_permissions` | | T11 | The admin setting above is the exception that allows fork `write` | `resolve_token_permissions` | | T12 | `pull_request_target` runs are not downgraded, even from a public fork | `resolve_token_permissions` | -| T13 | From a fork, no secret except `GITHUB_TOKEN` reaches the runner | `secrets_reach_runner` | -| E1 | `pull_request`: `GITHUB_SHA` is the merge commit, `GITHUB_REF` is `refs/pull/N/merge`; skipped on a merge conflict | `pull_request_event_context` | +| T13 | From a fork, no secret except `GITHUB_TOKEN` reaches the runner, except that `pull_request_target` (a privileged trigger that may access referenced secrets) is not withheld | `secrets_reach_runner` | +| E1 | `pull_request`: `GITHUB_SHA` is the merge commit, `GITHUB_REF` is `refs/pull/N/merge` (the destination branch when closed by merge); skipped on a merge conflict | `pull_request_event_context` | | E2 | Both triggers run by default for `opened`, `synchronize`, `reopened` | `pull_request_event_context` | | E3 | Events caused by the repository token create no run, except `workflow_dispatch`/`repository_dispatch` (always) and `pull_request` `opened`/`synchronize`/`reopened` (approval-required, behind the docs' version flag) | `workflow_run_for_token_event` | | E4 | `pull_request_target` runs against the default branch of the base repository (base branch on GHES before 3.20), never the merge commit | `pull_request_event_context` | @@ -56,7 +56,7 @@ cases use `contents: read, packages: read` as an illustration. ## Conformance -`evidence/semantics/github-token-permissions/conformance.json` holds 38 cases. Each +`evidence/semantics/github-token-permissions/conformance.json` holds 40 cases. Each cites rules; `check_conformance` fails when the model disagrees with a case, when a case cites a rule no source statement supports, and when a rule that source statements support has no case. Cases include the error results the model must produce (for diff --git a/evidence/semantics/github-token-permissions/accounting.json b/evidence/semantics/github-token-permissions/accounting.json index 78e920cc..5064e108 100644 --- a/evidence/semantics/github-token-permissions/accounting.json +++ b/evidence/semantics/github-token-permissions/accounting.json @@ -1,11 +1,11 @@ { "commit": "56fcfa816f27bca239e5d39fff0d4f74f77ec995", - "files": 9, - "occurrences": 30, + "files": 10, + "occurrences": 32, "policy_adopted": false, - "propositions": 34, + "propositions": 36, "propositions_preserved_without_model_rule": 4, - "propositions_with_model_rule": 30, + "propositions_with_model_rule": 32, "repository": "github/docs", "review_status": "PROPOSED", "rules_supported": [ diff --git a/evidence/semantics/github-token-permissions/annotations.json b/evidence/semantics/github-token-permissions/annotations.json index 6cfe9ada..b80b3362 100644 --- a/evidence/semantics/github-token-permissions/annotations.json +++ b/evidence/semantics/github-token-permissions/annotations.json @@ -222,7 +222,7 @@ "propositions": [ { "ambiguities": [], - "formalization": "NOT RUN unless event in {workflow_dispatch, repository_dispatch}.", + "formalization": "NO_RUN unless event in {workflow_dispatch, repository_dispatch}, or the approval flag applies and event = pull_request with activity in {opened, synchronize, reopened} (APPROVAL_REQUIRED).", "preserved_differences": [], "rules": [ "E3" @@ -231,7 +231,8 @@ "action": "create", "consequences": [], "exceptions": [ - "workflow_dispatch and repository_dispatch events always create workflow runs" + "workflow_dispatch and repository_dispatch events always create workflow runs", + "pull_request opened, synchronize and reopened events create approval-required runs when the actions-github-token-pull-request-approval version flag applies" ], "modality": "DESCRIPTIVE", "object": "a new workflow run", @@ -342,6 +343,54 @@ "start_line": 6, "versions": [] }, + { + "conditions": [], + "content_sha256": "bfc40bec3a30979187ed375da6534858b3012d5813ceb6418bdd755ca0769ec6", + "end_line": 111, + "expect": "access to referenced secrets", + "heading_ancestry": [ + "Mitigating the risks of untrusted code checkout" + ], + "include_chain": [], + "path": "content/actions/reference/security/secure-use.md", + "propositions": [ + { + "ambiguities": [ + "The page does not say which secrets or conditions; workflow_run is not modelled" + ], + "formalization": "Privileged triggers: a fork pull_request_target run is not subject to the fork secret withholding of the forked-secrets rule.", + "preserved_differences": [], + "rules": [ + "T13" + ], + "semantic_ast": { + "action": "have", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "repository write access and access to referenced secrets", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [ + "Stated as \"may\"; the model treats pull_request_target as able to reach secrets" + ], + "scope": [ + "github/docs", + "content/actions/reference/security/secure-use.md" + ], + "subject": "pull_request_target and workflow_run workflows" + } + } + ], + "semantic_kind": "FACT", + "start_line": 111, + "versions": [ + "fpt", + "ghes", + "ghec" + ] + }, { "conditions": [], "content_sha256": "482aa4bf0e2ad3061611c010143168a79cf1664126951c177722f6352662481e", @@ -1168,6 +1217,47 @@ "start_line": 466, "versions": [] }, + { + "conditions": [], + "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", + "end_line": 469, + "expect": "varies for a closed pull request", + "heading_ancestry": [ + "`pull_request`" + ], + "include_chain": [], + "path": "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "propositions": [ + { + "ambiguities": [], + "formalization": "ref(pull_request, closed, merged) = destination branch; otherwise refs/pull/N/merge.", + "preserved_differences": [], + "rules": [ + "E1" + ], + "semantic_ast": { + "action": "set", + "consequences": [], + "exceptions": [], + "modality": "DESCRIPTIVE", + "object": "GITHUB_REF to refs/pull/PULL_REQUEST_NUMBER/merge when closed unmerged and to the fully qualified destination branch ref when closed by merge", + "parameters": [], + "polarity": "POSITIVE", + "preconditions": [], + "qualifiers": [], + "scope": [ + "github/docs", + "content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md", + "pull_request" + ], + "subject": "pull_request event for a closed pull request" + } + } + ], + "semantic_kind": "FACT", + "start_line": 469, + "versions": [] + }, { "conditions": [], "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", @@ -1254,7 +1344,7 @@ }, { "conditions": [ - "ifversion ghes < 3.20" + "GHES before 3.20 selects the pull request base branch; GitHub.com and later GHES select the default branch" ], "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", "end_line": 669, @@ -1338,7 +1428,7 @@ }, { "conditions": [ - "ifversion ghes < 3.20 selects the base of the pull request instead" + "GHES before 3.20 selects the pull request base branch; GitHub.com and later GHES select the default branch" ], "content_sha256": "69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50", "end_line": 676, diff --git a/evidence/semantics/github-token-permissions/conformance.json b/evidence/semantics/github-token-permissions/conformance.json index dc0d457a..d48e4dd3 100644 --- a/evidence/semantics/github-token-permissions/conformance.json +++ b/evidence/semantics/github-token-permissions/conformance.json @@ -880,6 +880,45 @@ "rules": [ "E3" ] + }, + { + "expect": { + "reaches_runner": true + }, + "given": { + "event": "pull_request_target", + "from_fork": true, + "name": "DEPLOY_KEY" + }, + "id": "T13-pull-request-target-fork-secret-reachable", + "kind": "secret", + "rules": [ + "T13" + ] + }, + { + "expect": { + "context": { + "default_activity_types": [ + "opened", + "synchronize", + "reopened" + ], + "ref": "fully qualified ref of the branch the pull request was merged into", + "runs_against": "merge commit of the pull request", + "sha": "last merge commit on the GITHUB_REF branch", + "skipped_on_merge_conflict": true + } + }, + "given": { + "closed_by_merge": true, + "event": "pull_request" + }, + "id": "E1-pull-request-closed-by-merge-ref", + "kind": "event-context", + "rules": [ + "E1" + ] } ], "default_note": "Defaults are caller-supplied illustrations (contents:read, packages:read); the pinned pages do not enumerate the restricted/permissive presets.", diff --git a/evidence/semantics/github-token-permissions/occurrences.jsonl b/evidence/semantics/github-token-permissions/occurrences.jsonl index f0005e10..bf341706 100644 --- a/evidence/semantics/github-token-permissions/occurrences.jsonl +++ b/evidence/semantics/github-token-permissions/occurrences.jsonl @@ -2,9 +2,10 @@ {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"DESCRIPTIVE: token(j).repository = workflow(j).repository; token is an installation access token of the GitHub App installed on that repository.","heading_ancestry":["About the `GITHUB_TOKEN`"],"id":"semantic-occurrence:66166e598b41c65fa708a3a1ac2739f60e067b96f49d94c9b7e6e1ee8102ec52","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"be","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"a GitHub App installation access token limited to the repository that contains the workflow","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Installed when GitHub Actions is enabled"],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8","end_line":17,"path":"content/actions/concepts/security/github_token.md","repository":"github/docs","span_sha256":"cf8029260f82f0211bcb5337bf6724a6e2a7e2cf38ba63e9c2915aa2d5ed3d55","start_line":17},"versions":["fpt","ghes","ghec"]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"DESCRIPTIVE: valid(token(j), t) iff t < min(end(j), start(j) + lifetime(runner(j))).","heading_ancestry":["About the `GITHUB_TOKEN`"],"id":"semantic-occurrence:555cabccd7097b33829d9c917405a67290c32f72aa5c23e4b8a82adebf546272","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"expire","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"when the job finishes or after its effective maximum lifetime","parameters":[],"polarity":"POSITIVE","preconditions":["GitHub fetches an installation access token before each job begins"],"qualifiers":[],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8","end_line":19,"path":"content/actions/concepts/security/github_token.md","repository":"github/docs","span_sha256":"755f5722abdd72280c125b4e223b7fe45e00e382cedd2042116eca49ec3cdf7d","start_line":19},"versions":["fpt","ghes","ghec"]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"DESCRIPTIVE: lifetime(github-hosted) <= 6h because maximum job execution time is 6h. DESCRIPTIVE: lifetime(self-hosted) <= 24h; job duration up to 5 days does not extend it.","heading_ancestry":["About the `GITHUB_TOKEN`"],"id":"semantic-occurrence:f7d0309d251bdbf0b7035c5d555dcc566080dab503ce1be84a5bb99c730b6d4d","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"live","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"at most 6 hours on GitHub-hosted runners","parameters":[],"polarity":"POSITIVE","preconditions":["GitHub-hosted runner"],"qualifiers":[],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"e9bad7ea7f8cfb23dd27a6be3df7b9ee586b63490890e4c6bf1a9ccbdc9d29f8","end_line":24,"path":"content/actions/concepts/security/github_token.md","repository":"github/docs","span_sha256":"9aaa82b3af316d70ff41ef6f70be48c23fcb87d0a21cf12064de30b08c40499c","start_line":23},"versions":["fpt","ghes","ghec"]} -{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"NOT RUN unless event in {workflow_dispatch, repository_dispatch}.","heading_ancestry":["When `GITHUB_TOKEN` triggers workflow runs"],"id":"semantic-occurrence:493494a1cbc9c3d281c61a2efa708ef7adc8970329bddebaf4e2b4f1b9cc95ac","include_chain":["content/actions/concepts/security/github_token.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":["workflow_dispatch and repository_dispatch events always create workflow runs"],"modality":"DESCRIPTIVE","object":"a new workflow run","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"events triggered by the repository GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"2077c4672482dd746c60cd2f75e62e394dd668b74fdd73338d5a895523fafdef","end_line":3,"path":"data/reusables/actions/actions-do-not-trigger-workflows.md","repository":"github/docs","span_sha256":"7ef4e8048d16eeab7d8c9324ede62ede8e352a1a04272c463df83030e18c20a7","start_line":1},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"NO_RUN unless event in {workflow_dispatch, repository_dispatch}, or the approval flag applies and event = pull_request with activity in {opened, synchronize, reopened} (APPROVAL_REQUIRED).","heading_ancestry":["When `GITHUB_TOKEN` triggers workflow runs"],"id":"semantic-occurrence:493494a1cbc9c3d281c61a2efa708ef7adc8970329bddebaf4e2b4f1b9cc95ac","include_chain":["content/actions/concepts/security/github_token.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":["workflow_dispatch and repository_dispatch events always create workflow runs","pull_request opened, synchronize and reopened events create approval-required runs when the actions-github-token-pull-request-approval version flag applies"],"modality":"DESCRIPTIVE","object":"a new workflow run","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"events triggered by the repository GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"2077c4672482dd746c60cd2f75e62e394dd668b74fdd73338d5a895523fafdef","end_line":3,"path":"data/reusables/actions/actions-do-not-trigger-workflows.md","repository":"github/docs","span_sha256":"7ef4e8048d16eeab7d8c9324ede62ede8e352a1a04272c463df83030e18c20a7","start_line":1},"versions":[]} {"authority_class":"PLATFORM","conditions":["ifversion actions-github-token-pull-request-approval"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"APPROVAL_REQUIRED iff event = pull_request and activity in {opened, synchronize, reopened} and flag actions-github-token-pull-request-approval; labeled, edited, closed => NO_RUN.","heading_ancestry":["When `GITHUB_TOKEN` triggers workflow runs"],"id":"semantic-occurrence:eaa6eff3d3bebf5705c804913c97e84d48cfe0fba34501237ac405389e5f86d4","include_chain":["content/actions/concepts/security/github_token.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"workflow runs in an approval-required state","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["A user with write access can start the runs by selecting Approve workflows to run","Other pull_request activity types do not create workflow runs"],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"pull_request events with opened, synchronize or reopened activity types caused by GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"2077c4672482dd746c60cd2f75e62e394dd668b74fdd73338d5a895523fafdef","end_line":4,"path":"data/reusables/actions/actions-do-not-trigger-workflows.md","repository":"github/docs","span_sha256":"73ea36f2060cb14ba33029dfae002a42a1eaef3a25f35209b97a3212a4c873b9","start_line":4},"versions":[]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"NOT RUN: example instance of the general rule for all other events.","heading_ancestry":["When `GITHUB_TOKEN` triggers workflow runs"],"id":"semantic-occurrence:278a06109127ce63a350310b02cb8eb4c464aa1eee57fc386928e3935dd9dd2b","include_chain":["content/actions/concepts/security/github_token.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"a new push-triggered workflow run","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":["Prevents accidental recursive workflow runs"],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"a push made with the repository GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"2077c4672482dd746c60cd2f75e62e394dd668b74fdd73338d5a895523fafdef","end_line":6,"path":"data/reusables/actions/actions-do-not-trigger-workflows.md","repository":"github/docs","span_sha256":"b7f69697e50645460bc03ab1e2f32efcb98b1be1e5b03400439272b7ebb051db","start_line":6},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Privileged triggers: a fork pull_request_target run is not subject to the fork secret withholding of the forked-secrets rule.","heading_ancestry":["Mitigating the risks of untrusted code checkout"],"id":"semantic-occurrence:3051e29e61aa328f62a36113db9d8f86f1fc7038a8b9f7d6162cd72d2ee9fbd7","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"have","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"repository write access and access to referenced secrets","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Stated as \"may\"; the model treats pull_request_target as able to reach secrets"],"scope":["github/docs","content/actions/reference/security/secure-use.md"],"subject":"pull_request_target and workflow_run workflows"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"bfc40bec3a30979187ed375da6534858b3012d5813ceb6418bdd755ca0769ec6","end_line":111,"path":"content/actions/reference/security/secure-use.md","repository":"github/docs","span_sha256":"bf1ad99a039c530d9e53556eedc94c47b8f83568454b136515b3fb3c17e7c6c3","start_line":111},"versions":["fpt","ghes","ghec"]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"reaches_runner(name, fork) iff name is GITHUB_TOKEN or not fork.","heading_ancestry":[],"id":"semantic-occurrence:91d1b848d0dcc627ae4746927e7adfcaaa81af8cabcf68847e5e624efba98458","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"pass","consequences":[],"exceptions":["GITHUB_TOKEN"],"modality":"DESCRIPTIVE","object":"secrets other than GITHUB_TOKEN to the runner","parameters":[],"polarity":"NEGATIVE","preconditions":["Workflow triggered from a forked repository"],"qualifiers":[],"scope":["github/docs","data/reusables/actions/forked-secrets.md"],"subject":"GitHub Actions"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"482aa4bf0e2ad3061611c010143168a79cf1664126951c177722f6352662481e","end_line":1,"path":"data/reusables/actions/forked-secrets.md","repository":"github/docs","span_sha256":"482aa4bf0e2ad3061611c010143168a79cf1664126951c177722f6352662481e","start_line":1},"versions":[]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"In a fork run, permissions can add or remove read; write cannot be granted. write grantable in fork runs iff admin selected Send write tokens to workflows from pull requests.","heading_ancestry":["`permissions`","Changing the permissions in a forked repository"],"id":"semantic-occurrence:4901fefec53a987e9ceb443a03b3d54b79f2fbe9e5e364915d3e0cac32dd9938","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"add or remove","consequences":[],"exceptions":[],"modality":"MAY","object":"read permissions for forked repositories with the permissions key","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/forked-write-permission.md"],"subject":"workflow author"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"5c090d30bda583faa17770216757dd1c1d006de72c9dec49a11fc34960111347","end_line":1,"path":"data/reusables/actions/forked-write-permission.md","repository":"github/docs","span_sha256":"5c090d30bda583faa17770216757dd1c1d006de72c9dec49a11fc34960111347","start_line":1},"versions":[]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"level(scope) in {none, read, write}, subject to per-scope restrictions.","heading_ancestry":["Defining access for the `GITHUB_TOKEN` scopes"],"id":"semantic-occurrence:aa8934c774956f14647220854bef8ca3eafbea0f32d35d3260772dc4d987e0dd","include_chain":["content/actions/reference/workflows-and-actions/workflow-syntax.md"],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"accept","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"read, write or none as the value of each available permission","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/github-token-available-permissions.md"],"subject":"permissions key"},"semantic_kind":"DEFINITION","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"f33d4c386b317098612b0a6ac15e0cf8a3a66d585001bccd0e69bdc607332d33","end_line":1,"path":"data/reusables/actions/github-token-available-permissions.md","repository":"github/docs","span_sha256":"80cb617e60fa38f8eb8f13557563e1a1467da02ceec2cbb782614311b28cf812","start_line":1},"versions":[]} @@ -22,9 +23,10 @@ {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"sha(pull_request) = merge commit, not the pull request head commit.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:a8161c3133bb0e9792816592a72c218e00b9071c3bbe654c755e9449ffde1230","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_SHA to the last merge commit on the GITHUB_REF branch and GITHUB_REF to refs/pull/PULL_REQUEST_NUMBER/merge","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Consumers needing the head commit must read it from the event payload"],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request event"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":462,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"315b89dd09cd81f86d786a7db2e486df12cb872e919102aa9b7de324e0f9f1b2","start_line":462},"versions":[]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"default types = {opened, synchronize, reopened}; others require the types keyword.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:5ad49b8e8364302cebec7bf9a5586233002fad28b303cff241e7551fa65d72bc","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"by default only for opened, synchronize and reopened activity types","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request workflow"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":465,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"cfe41b2e44d1c58c50737be3192181408445cf161b23bbda848ccff635b59d40","start_line":465},"versions":[]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"skipped(pull_request) iff merge conflict; skipped(pull_request_target) is false.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:8751bea4bc24f0a3900ef001b9383ce95908c5e5faa693b8619319b7160b8bfe","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":["pull_request_target runs even with a merge conflict"],"modality":"DESCRIPTIVE","object":"on a pull request that has a merge conflict","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request workflow"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":466,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"fd1e85086384062c8bf094330b5add1b5d4ab2dc8ce6437ec4582bd10e49a68a","start_line":466},"versions":[]} +{"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"ref(pull_request, closed, merged) = destination branch; otherwise refs/pull/N/merge.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:d9a66fa71e23a728ccba5ac2bdc57618eaf3e7a018112c23f5e21039e92bd9d0","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_REF to refs/pull/PULL_REQUEST_NUMBER/merge when closed unmerged and to the fully qualified destination branch ref when closed by merge","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request event for a closed pull request"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":469,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"4cb3a6bcb9c431feb63354131002b70ef928d9523eafbd94d49022b5d2d0ecac","start_line":469},"versions":[]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Preserved without a model rule.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:bcd282fc5bb4f2faa54a923fb98a13162db65c3329793d248452f490e0d7dc10","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"be","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"empty for merged pull requests and pull requests from forked repositories","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request webhook event payload"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":467,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"6575d80f34020008921c61bbf79f0c50cdadb3364e38014a1c04162356940ae8","start_line":467},"versions":[]} {"authority_class":"PLATFORM","conditions":["ifversion actions-github-token-pull-request-approval"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Same rule as the reusable at actions-do-not-trigger-workflows.md; other GITHUB_TOKEN-triggered events create no runs except workflow_dispatch and repository_dispatch.","heading_ancestry":["`pull_request`"],"id":"semantic-occurrence:e686dc1efbb0bdb1235517b83b805ef3448366f10e5bae0ccee3f63c5f509a1f","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"workflow runs that require approval for opened, synchronize and reopened","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request events caused by a workflow using GITHUB_TOKEN"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":468,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"956ca13dae815053f5453d5b21c647f60a75fdc773f31f16bd309032f8c7a555","start_line":468},"versions":[]} -{"authority_class":"PLATFORM","conditions":["ifversion ghes < 3.20"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"sha(pull_request_target) = base-side commit; never the pull request head or merge commit.","heading_ancestry":["`pull_request_target`"],"id":"semantic-occurrence:20c206e83c3f58889b9e61c33d9b1ff9fedbfd7f4f47ddac7ce4e9a0ffead31d","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_SHA to the last commit on the default branch (the pull request base branch on GHES before 3.20)","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":669,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"16854493714afa4c9a43307c6f0178ca1067431a7940fad539b4825d2f1e48b4","start_line":665},"versions":[]} +{"authority_class":"PLATFORM","conditions":["GHES before 3.20 selects the pull request base branch; GitHub.com and later GHES select the default branch"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"sha(pull_request_target) = base-side commit; never the pull request head or merge commit.","heading_ancestry":["`pull_request_target`"],"id":"semantic-occurrence:20c206e83c3f58889b9e61c33d9b1ff9fedbfd7f4f47ddac7ce4e9a0ffead31d","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_SHA to the last commit on the default branch (the pull request base branch on GHES before 3.20)","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":669,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"16854493714afa4c9a43307c6f0178ca1067431a7940fad539b4825d2f1e48b4","start_line":665},"versions":[]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"default types identical to pull_request.","heading_ancestry":["`pull_request_target`"],"id":"semantic-occurrence:f3b789bb871e1629e93f15f7db09586e6abb95f646808be603b4f57e0b56413d","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"by default only for opened, synchronize and reopened activity types","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target workflow"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":672,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"c75d965d62ba1f5840cdd9f2534de51a1923fa58744d2914b1e5fc5745e3896f","start_line":672},"versions":[]} -{"authority_class":"PLATFORM","conditions":["ifversion ghes < 3.20 selects the base of the pull request instead"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Prevents execution of unsafe head code; avoid when building or running pull request code.","heading_ancestry":["`pull_request_target`"],"id":"semantic-occurrence:7401611a017c13b68ab6b1ceb56a5a7344c60b6f40ab3105e0471d7a4540c61a","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"in the context of the default branch of the base repository rather than the merge commit","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Avoid when you need to build or run code from the pull request"],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":676,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"c31df378542649254e321519f4e4b1a353f02629e828af6e4848019f5bb226c3","start_line":676},"versions":[]} +{"authority_class":"PLATFORM","conditions":["GHES before 3.20 selects the pull request base branch; GitHub.com and later GHES select the default branch"],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Prevents execution of unsafe head code; avoid when building or running pull request code.","heading_ancestry":["`pull_request_target`"],"id":"semantic-occurrence:7401611a017c13b68ab6b1ceb56a5a7344c60b6f40ab3105e0471d7a4540c61a","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"in the context of the default branch of the base repository rather than the merge commit","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Avoid when you need to build or run code from the pull request"],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"},"semantic_kind":"FACT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":676,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"c31df378542649254e321519f4e4b1a353f02629e828af6e4848019f5bb226c3","start_line":676},"versions":[]} {"authority_class":"PLATFORM","conditions":[],"extraction":{"method":"authored-span-annotations","version":"token-semantics.v1"},"formalization":"Preserved without a model rule: the patterns are not enumerated.","heading_ancestry":["`pull_request_target`"],"id":"semantic-occurrence:586530a3f463a781c6c0d0a5907493f5e2fed829ba0217735fae1c5dc3a7c13b","include_chain":[],"page_applications":[],"products":["GitHub Actions"],"review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-occurrence.v1","semantic_ast":{"action":"trigger","consequences":[],"exceptions":[],"modality":"MAY","object":"workflows with pull_request_target","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"branches whose names look similar to SHAs"},"semantic_kind":"CONSTRAINT","source":{"commit":"56fcfa816f27bca239e5d39fff0d4f74f77ec995","content_sha256":"69e251c1510f84d7b0a05ff8a4146456e657f175133081213805e02b0a655f50","end_line":678,"path":"content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","repository":"github/docs","span_sha256":"664f23c23d26dc06ca1440bb15f33e397f2599881c83cc1f2e3085ebb07e62d2","start_line":678},"versions":[]} diff --git a/evidence/semantics/github-token-permissions/propositions.jsonl b/evidence/semantics/github-token-permissions/propositions.jsonl index b15dbb33..4760acfe 100644 --- a/evidence/semantics/github-token-permissions/propositions.jsonl +++ b/evidence/semantics/github-token-permissions/propositions.jsonl @@ -3,9 +3,10 @@ {"ambiguities":[],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:1e692f20ab466d4847bb5199d509fe58a803613d53d68ed4172931ccb1d427f3","occurrence_ids":["semantic-occurrence:555cabccd7097b33829d9c917405a67290c32f72aa5c23e4b8a82adebf546272"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"expire","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"when the job finishes or after its effective maximum lifetime","parameters":[],"polarity":"POSITIVE","preconditions":["GitHub fetches an installation access token before each job begins"],"qualifiers":[],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"}} {"ambiguities":[],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:dbd8e10bcb0307deaa127468d61f1bae09c8e88e4392c00fdd7305641d3fc0a5","occurrence_ids":["semantic-occurrence:f7d0309d251bdbf0b7035c5d555dcc566080dab503ce1be84a5bb99c730b6d4d"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"live","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"at most 6 hours on GitHub-hosted runners","parameters":[],"polarity":"POSITIVE","preconditions":["GitHub-hosted runner"],"qualifiers":[],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"}} {"ambiguities":[],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:d55115079d5dababf0d8c4cf4354784e70cabff97fe120cab74f73664cf1b46a","occurrence_ids":["semantic-occurrence:f7d0309d251bdbf0b7035c5d555dcc566080dab503ce1be84a5bb99c730b6d4d"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"refresh","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"for at most 24 hours on self-hosted runners although jobs may run 5 days","parameters":[],"polarity":"POSITIVE","preconditions":["Self-hosted runner"],"qualifiers":["Use a personal access token or other authentication for jobs longer than 24 hours"],"scope":["github/docs","content/actions/concepts/security/github_token.md"],"subject":"GITHUB_TOKEN"}} -{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:482dff2ed5541a705110d5f77c3e07c8ac9900e344f6267ff0f818454c8bca03","occurrence_ids":["semantic-occurrence:493494a1cbc9c3d281c61a2efa708ef7adc8970329bddebaf4e2b4f1b9cc95ac"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":["workflow_dispatch and repository_dispatch events always create workflow runs"],"modality":"DESCRIPTIVE","object":"a new workflow run","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"events triggered by the repository GITHUB_TOKEN"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:2bf6040267fc756924cc2b74e9ec607f694d86255e562f76497b446c962fba33","occurrence_ids":["semantic-occurrence:493494a1cbc9c3d281c61a2efa708ef7adc8970329bddebaf4e2b4f1b9cc95ac"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":["workflow_dispatch and repository_dispatch events always create workflow runs","pull_request opened, synchronize and reopened events create approval-required runs when the actions-github-token-pull-request-approval version flag applies"],"modality":"DESCRIPTIVE","object":"a new workflow run","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"events triggered by the repository GITHUB_TOKEN"}} {"ambiguities":[],"applicability":["ifversion actions-github-token-pull-request-approval"],"authority_order":["PLATFORM"],"id":"semantic-proposition:65d8476f96920f977911a93914a55b0a9698e7d9aba96f0e0f7220151ce5bb14","occurrence_ids":["semantic-occurrence:eaa6eff3d3bebf5705c804913c97e84d48cfe0fba34501237ac405389e5f86d4"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"workflow runs in an approval-required state","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["A user with write access can start the runs by selecting Approve workflows to run","Other pull_request activity types do not create workflow runs"],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"pull_request events with opened, synchronize or reopened activity types caused by GITHUB_TOKEN"}} {"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:3cafad63f534e6ae65484402fafc349699de2c86cc479abd6422cc62ddff2397","occurrence_ids":["semantic-occurrence:278a06109127ce63a350310b02cb8eb4c464aa1eee57fc386928e3935dd9dd2b"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"a new push-triggered workflow run","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":["Prevents accidental recursive workflow runs"],"scope":["github/docs","data/reusables/actions/actions-do-not-trigger-workflows.md"],"subject":"a push made with the repository GITHUB_TOKEN"}} +{"ambiguities":["The page does not say which secrets or conditions; workflow_run is not modelled"],"applicability":["fpt","ghes","ghec"],"authority_order":["PLATFORM"],"id":"semantic-proposition:759c38b8c618b6a7c66b00ef9258fb982b4512bbab6060c0b3660eacb68cddbe","occurrence_ids":["semantic-occurrence:3051e29e61aa328f62a36113db9d8f86f1fc7038a8b9f7d6162cd72d2ee9fbd7"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"have","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"repository write access and access to referenced secrets","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Stated as \"may\"; the model treats pull_request_target as able to reach secrets"],"scope":["github/docs","content/actions/reference/security/secure-use.md"],"subject":"pull_request_target and workflow_run workflows"}} {"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:2569b1ce88484c9d3ff83b61241cf6d44a4ab2585190674f409cb7263a4c4afc","occurrence_ids":["semantic-occurrence:91d1b848d0dcc627ae4746927e7adfcaaa81af8cabcf68847e5e624efba98458"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"pass","consequences":[],"exceptions":["GITHUB_TOKEN"],"modality":"DESCRIPTIVE","object":"secrets other than GITHUB_TOKEN to the runner","parameters":[],"polarity":"NEGATIVE","preconditions":["Workflow triggered from a forked repository"],"qualifiers":[],"scope":["github/docs","data/reusables/actions/forked-secrets.md"],"subject":"GitHub Actions"}} {"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:e93d80cf9d21b9fdda383c6d7026ef8638c6d66eddac9e0f89c2da58c0f2496b","occurrence_ids":["semantic-occurrence:4901fefec53a987e9ceb443a03b3d54b79f2fbe9e5e364915d3e0cac32dd9938"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"add or remove","consequences":[],"exceptions":[],"modality":"MAY","object":"read permissions for forked repositories with the permissions key","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","data/reusables/actions/forked-write-permission.md"],"subject":"workflow author"}} {"ambiguities":["\"typically\" is not defined; the model treats the setting as the only exception"],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:d3ca3d5ae90bb60fb93e401b70198a56673d437c57330953318b3af2ff25de95","occurrence_ids":["semantic-occurrence:4901fefec53a987e9ceb443a03b3d54b79f2fbe9e5e364915d3e0cac32dd9938"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"grant","consequences":[],"exceptions":["An admin selected Send write tokens to workflows from pull requests"],"modality":"MAY","object":"write access in a forked repository with the permissions key","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["The docs say \"typically you can't\", so the normative strength is hedged"],"scope":["github/docs","data/reusables/actions/forked-write-permission.md"],"subject":"workflow author"}} @@ -26,9 +27,10 @@ {"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:3dba592ed0b4100d0fccac9aa4d8791ae9f148ed9311fda8fe8062cdd760f885","occurrence_ids":["semantic-occurrence:a8161c3133bb0e9792816592a72c218e00b9071c3bbe654c755e9449ffde1230"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_SHA to the last merge commit on the GITHUB_REF branch and GITHUB_REF to refs/pull/PULL_REQUEST_NUMBER/merge","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Consumers needing the head commit must read it from the event payload"],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request event"}} {"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:4fd2b55acf794aa6b999bb2faeb548c07421e46d24ea375cb7985a221a806a79","occurrence_ids":["semantic-occurrence:5ad49b8e8364302cebec7bf9a5586233002fad28b303cff241e7551fa65d72bc"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"by default only for opened, synchronize and reopened activity types","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request workflow"}} {"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:ba128e2185b0141dfece458a52d7b71e3c9041cc63b7f8d2c7220bb947fa5708","occurrence_ids":["semantic-occurrence:8751bea4bc24f0a3900ef001b9383ce95908c5e5faa693b8619319b7160b8bfe"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":["pull_request_target runs even with a merge conflict"],"modality":"DESCRIPTIVE","object":"on a pull request that has a merge conflict","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request workflow"}} +{"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:116580748f9bce8291f3fd44aae4711e8655daa8260533773e04c1a6055468c5","occurrence_ids":["semantic-occurrence:d9a66fa71e23a728ccba5ac2bdc57618eaf3e7a018112c23f5e21039e92bd9d0"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_REF to refs/pull/PULL_REQUEST_NUMBER/merge when closed unmerged and to the fully qualified destination branch ref when closed by merge","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request event for a closed pull request"}} {"ambiguities":["Contradicts common use of github.event.pull_request fields in same-repository runs and is not modelled; verify against a real run before relying on it"],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:b465df3f413413fe2c5ded1f5e2d7cbd368499fb59d48d1f3492577ab767ab2e","occurrence_ids":["semantic-occurrence:bcd282fc5bb4f2faa54a923fb98a13162db65c3329793d248452f490e0d7dc10"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"be","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"empty for merged pull requests and pull requests from forked repositories","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request webhook event payload"}} {"ambiguities":[],"applicability":["ifversion actions-github-token-pull-request-approval"],"authority_order":["PLATFORM"],"id":"semantic-proposition:14f0e55241cee49c2b792b3f8da2153794a7ed7c1b0afdf2084dc74437d6d0de","occurrence_ids":["semantic-occurrence:e686dc1efbb0bdb1235517b83b805ef3448366f10e5bae0ccee3f63c5f509a1f"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"create","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"workflow runs that require approval for opened, synchronize and reopened","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request"],"subject":"pull_request events caused by a workflow using GITHUB_TOKEN"}} -{"ambiguities":[],"applicability":["ifversion ghes < 3.20"],"authority_order":["PLATFORM"],"id":"semantic-proposition:445765dca582770c0ddf33c6c7083c750962ef66faf7f836217fb1f14af9b6cd","occurrence_ids":["semantic-occurrence:20c206e83c3f58889b9e61c33d9b1ff9fedbfd7f4f47ddac7ce4e9a0ffead31d"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_SHA to the last commit on the default branch (the pull request base branch on GHES before 3.20)","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"}} +{"ambiguities":[],"applicability":["GHES before 3.20 selects the pull request base branch; GitHub.com and later GHES select the default branch"],"authority_order":["PLATFORM"],"id":"semantic-proposition:445765dca582770c0ddf33c6c7083c750962ef66faf7f836217fb1f14af9b6cd","occurrence_ids":["semantic-occurrence:20c206e83c3f58889b9e61c33d9b1ff9fedbfd7f4f47ddac7ce4e9a0ffead31d"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"set","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"GITHUB_SHA to the last commit on the default branch (the pull request base branch on GHES before 3.20)","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"}} {"ambiguities":[],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:7e9d2d42a5cd7ff280534613bc878f777bfa901d88a1ca3c44922c3136e10b7b","occurrence_ids":["semantic-occurrence:f3b789bb871e1629e93f15f7db09586e6abb95f646808be603b4f57e0b56413d"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"by default only for opened, synchronize and reopened activity types","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target workflow"}} -{"ambiguities":[],"applicability":["ifversion ghes < 3.20 selects the base of the pull request instead"],"authority_order":["PLATFORM"],"id":"semantic-proposition:e2c059f42e2630791d78203a5f5f84c79401329be349b3e5c71cfc07854e10da","occurrence_ids":["semantic-occurrence:7401611a017c13b68ab6b1ceb56a5a7344c60b6f40ab3105e0471d7a4540c61a"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"in the context of the default branch of the base repository rather than the merge commit","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Avoid when you need to build or run code from the pull request"],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"}} +{"ambiguities":[],"applicability":["GHES before 3.20 selects the pull request base branch; GitHub.com and later GHES select the default branch"],"authority_order":["PLATFORM"],"id":"semantic-proposition:e2c059f42e2630791d78203a5f5f84c79401329be349b3e5c71cfc07854e10da","occurrence_ids":["semantic-occurrence:7401611a017c13b68ab6b1ceb56a5a7344c60b6f40ab3105e0471d7a4540c61a"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"run","consequences":[],"exceptions":[],"modality":"DESCRIPTIVE","object":"in the context of the default branch of the base repository rather than the merge commit","parameters":[],"polarity":"POSITIVE","preconditions":[],"qualifiers":["Avoid when you need to build or run code from the pull request"],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"pull_request_target event"}} {"ambiguities":["Patterns are undefined in the source; no behaviour can be derived"],"applicability":[],"authority_order":["PLATFORM"],"id":"semantic-proposition:4a7fafbddd6c3dd85d900bea829f8ea3e4a88acb3d54f70f1e28a3a27d7848b5","occurrence_ids":["semantic-occurrence:586530a3f463a781c6c0d0a5907493f5e2fed829ba0217735fae1c5dc3a7c13b"],"omission_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"preserved_differences":[],"primary_review":{"evidence_reference":null,"reviewer":null,"status":"PROPOSED"},"schema":"ges.semantic-proposition.v1","semantic_ast":{"action":"trigger","consequences":[],"exceptions":[],"modality":"MAY","object":"workflows with pull_request_target","parameters":[],"polarity":"NEGATIVE","preconditions":[],"qualifiers":[],"scope":["github/docs","content/actions/reference/workflows-and-actions/events-that-trigger-workflows.md","pull_request_target"],"subject":"branches whose names look similar to SHAs"}} diff --git a/ges/token_semantics.py b/ges/token_semantics.py index a5d10197..7e9f087c 100644 --- a/ges/token_semantics.py +++ b/ges/token_semantics.py @@ -142,11 +142,17 @@ def allows(permissions: dict[str, str], scope: str, needed: str) -> bool: return LEVELS.index(permissions.get(scope, 'none')) >= LEVELS.index(needed) -def pull_request_event_context(event: str, *, ghes_before_3_20: bool = False) -> dict[str, object]: - """Rules E1, E2, E4: what GITHUB_SHA and GITHUB_REF denote for the two PR triggers.""" +def pull_request_event_context(event: str, *, ghes_before_3_20: bool = False, + closed_by_merge: bool = False) -> dict[str, object]: + """Rules E1, E2, E4: what GITHUB_SHA and GITHUB_REF denote for the two PR triggers. + + `closed_by_merge` applies to `pull_request` closed activity: the ref is then the + fully qualified destination branch rather than the pull request merge ref. + """ if event == 'pull_request': return {'sha': 'last merge commit on the GITHUB_REF branch', - 'ref': 'refs/pull/PULL_REQUEST_NUMBER/merge', + 'ref': ('fully qualified ref of the branch the pull request was merged into' + if closed_by_merge else 'refs/pull/PULL_REQUEST_NUMBER/merge'), 'runs_against': 'merge commit of the pull request', 'default_activity_types': DEFAULT_PULL_REQUEST_ACTIVITY_TYPES, 'skipped_on_merge_conflict': True} @@ -188,9 +194,13 @@ def token_is_valid(runner: str, elapsed_hours: float, *, job_finished: bool) -> return not job_finished and elapsed_hours < limits[runner] -def secrets_reach_runner(name: str, *, from_fork: bool) -> bool: - """Rule T13: from a forked repository only GITHUB_TOKEN reaches the runner.""" - return name == 'GITHUB_TOKEN' or not from_fork +def secrets_reach_runner(name: str, *, from_fork: bool, event: str = 'pull_request') -> bool: + """Rule T13: from a forked repository only GITHUB_TOKEN reaches the runner. + + `pull_request_target` is a privileged trigger whose workflows may access referenced + secrets (secure-use reference), so it is not withheld. + """ + return name == 'GITHUB_TOKEN' or not from_fork or event == PULL_REQUEST_TARGET # --- source-bound ledger --------------------------------------------------------------- @@ -347,7 +357,8 @@ def _run_case(case: dict) -> dict: send_write_tokens=given.get('send_write_tokens', False), flags=flags) return {'allowed': allows(permissions, given['scope'], given['needed'])} if kind == 'event-context': - context = pull_request_event_context(given['event'], ghes_before_3_20=given.get('ghes_before_3_20', False)) + context = pull_request_event_context(given['event'], ghes_before_3_20=given.get('ghes_before_3_20', False), + closed_by_merge=given.get('closed_by_merge', False)) return {'context': {k: list(v) if isinstance(v, tuple) else v for k, v in context.items()}} if kind == 'token-event': return {'result': workflow_run_for_token_event( @@ -355,7 +366,7 @@ def _run_case(case: dict) -> dict: if kind == 'token-valid': return {'valid': token_is_valid(given['runner'], given['elapsed_hours'], job_finished=given['job_finished'])} if kind == 'secret': - return {'reaches_runner': secrets_reach_runner(given['name'], from_fork=given['from_fork'])} + return {'reaches_runner': secrets_reach_runner(given['name'], from_fork=given['from_fork'], event=given.get('event', 'pull_request'))} raise ValueError(f'unknown case kind {kind}') diff --git a/tests/test_token_semantics.py b/tests/test_token_semantics.py index 4a697b6a..a862322a 100644 --- a/tests/test_token_semantics.py +++ b/tests/test_token_semantics.py @@ -73,6 +73,23 @@ def test_read_all_is_clipped_to_scope_levels(self): self.assertEqual(read_all['vulnerability-alerts'], 'read') self.assertEqual(ts.apply_permissions_key('write-all', flags)['vulnerability-alerts'], 'read') + def test_pull_request_target_fork_run_can_reach_secrets_pull_request_cannot(self): + self.assertFalse(ts.secrets_reach_runner('DEPLOY_KEY', from_fork=True, event='pull_request')) + self.assertTrue(ts.secrets_reach_runner('DEPLOY_KEY', from_fork=True, event='pull_request_target')) + + def test_merged_closure_ref_is_the_destination_branch(self): + merged = ts.pull_request_event_context('pull_request', closed_by_merge=True)['ref'] + self.assertNotIn('refs/pull', merged) + self.assertIn('refs/pull', ts.pull_request_event_context('pull_request')['ref']) + + def test_version_split_statements_do_not_restrict_applicability_to_old_ghes(self): + for line in (ts.PROPOSITIONS.read_text(encoding='utf-8').splitlines()): + record = json.loads(line) + if record['semantic_ast']['subject'] == 'pull_request_target event': + self.assertTrue(any('GitHub.com and later GHES' in item for item in record['applicability']), + record['semantic_ast']['object']) + self.assertFalse(any(item == 'ifversion ghes < 3.20' for item in record['applicability'])) + def test_unknown_runner_rejected(self): with self.assertRaises(ValueError): ts.token_is_valid('hosted', 1.0, job_finished=False)