diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5d39bf3af82..84fa9f45187 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,9 +5,10 @@ internal/codespaces/ @cli/codespaces # Limit Package Security team ownership to the attestation command package and related integration tests pkg/cmd/attestation/ @cli/package-security -pkg/cmd/release/attestation @cli/package-security -pkg/cmd/release/verify @cli/package-security -pkg/cmd/release/verify-asset @cli/package-security +pkg/cmd/release/attestation/ @cli/package-security +pkg/cmd/release/verify/ @cli/package-security +pkg/cmd/release/verify-asset/ @cli/package-security +pkg/cmd/release/shared/ @cli/package-security test/integration/attestation-cmd @cli/package-security diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f30257878e9..ac32a67af3a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -48,7 +48,7 @@ jobs: - name: Filter SARIF for third-party code if: matrix.language == 'go' - uses: advanced-security/filter-sarif@bc96d9fb9338c5b48cc440b1b4d0a350b26a20db # v1.0.0 + uses: advanced-security/filter-sarif@f3b8118a9349d88f7b1c0c488476411145b6270d # v1.0.1 with: patterns: | -third-party/** diff --git a/.github/workflows/detect-spam.yml b/.github/workflows/detect-spam.yml new file mode 100644 index 00000000000..3e8c4a61880 --- /dev/null +++ b/.github/workflows/detect-spam.yml @@ -0,0 +1,27 @@ +name: Spam Issue Detection +on: + issues: + types: [opened] + +permissions: + contents: none + issues: write + models: read + +jobs: + issue-spam: + runs-on: ubuntu-latest + environment: cli-automation + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Run spam detection + env: + GH_TOKEN: ${{ secrets.AUTOMATION_TOKEN }} + ISSUE_URL: ${{ github.event.issue.html_url }} + run: | + ./.github/workflows/scripts/spam-detection/process-issue.sh "$ISSUE_URL" + if [[ $? -ne 0 ]]; then + echo "error processing issue" + exit 1 + fi diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 9b22701a7d3..7772b381455 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,5 +1,9 @@ name: Unit and Integration Tests -on: [push, pull_request] +on: + push: + branches: + - trunk + pull_request: permissions: contents: read diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml new file mode 100644 index 00000000000..42d94077cfb --- /dev/null +++ b/.github/workflows/govulncheck.yml @@ -0,0 +1,30 @@ +name: Go Vulnerability Check +on: + schedule: + - cron: "0 0 * * 1" # Every Monday at midnight UTC +jobs: + govulncheck: + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + # `govulncheck -format sarif` exits successfully regardless of results, which are not in stdout. + # See https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck#hdr-Exit_codes for more information on exit codes. + - name: Check Go vulnerabilities + run: | + make + go run golang.org/x/vuln/cmd/govulncheck@d1f380186385b4f64e00313f31743df8e4b89a77 -mode=binary -format sarif bin/gh > gh.sarif + + - name: Upload SARIF report + uses: github/codeql-action/upload-sarif@9b02dc2f60288b463e7a66e39c78829b62780db7 # 2.22.1 + with: + sarif_file: gh.sarif diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 866dc3a2d1e..5281a46d009 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,6 +1,8 @@ name: Lint on: push: + branches: + - trunk paths: - "**.go" - go.mod @@ -14,14 +16,11 @@ on: - go.sum - ".github/licenses.tmpl" - "script/licenses*" - permissions: contents: read - jobs: lint: runs-on: ubuntu-latest - steps: - name: Check out code uses: actions/checkout@v4 @@ -62,3 +61,22 @@ jobs: export PATH=${GOROOT}/bin:$PATH go install github.com/google/go-licenses@5348b744d0983d85713295ea08a20cca1654a45e make licenses-check + + # Discover vulnerabilities within Go standard libraries used to build GitHub CLI using govulncheck. + govulncheck: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + # `govulncheck` exits unsuccessfully if vulnerabilities are found, providing results in stdout. + # See https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck#hdr-Exit_codes for more information on exit codes. + - name: Check Go vulnerabilities + run: | + make + go run golang.org/x/vuln/cmd/govulncheck@d1f380186385b4f64e00313f31743df8e4b89a77 -mode=binary bin/gh diff --git a/.github/workflows/scripts/spam-detection/check-issue-prompts.yml b/.github/workflows/scripts/spam-detection/check-issue-prompts.yml new file mode 100644 index 00000000000..b6728c7c1d4 --- /dev/null +++ b/.github/workflows/scripts/spam-detection/check-issue-prompts.yml @@ -0,0 +1,7 @@ +name: Detect spam +model: openai/gpt-4o-mini +messages: + - role: system + content: "" # Since it's not a fix value, it should be generated and replaced at runtime + - role: user + content: "" # This will be replaced at runtime diff --git a/.github/workflows/scripts/spam-detection/check-issue.sh b/.github/workflows/scripts/spam-detection/check-issue.sh new file mode 100755 index 00000000000..2f82eb4eacd --- /dev/null +++ b/.github/workflows/scripts/spam-detection/check-issue.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Check if an issue is spam or not and output "PASS" (not spam) or "FAIL" (spam). +# +# Regardless of the spam detection result, the script always exits with a zero +# exit code, unless there's a runtime error. +# +# This script must be run from the root directory of the repository. + +set -euo pipefail + +# Determine absolute path to script directory based on where it is called from. +# This allows the script to be run from any directory. +SPAM_DIR="$(dirname "$(realpath "$0")")" + +# Retrieve and prepare information about issue for detection +_issue_url="$1" +if [[ -z "$_issue_url" ]]; then + echo "error: issue URL is empty" >&2 + exit 1 +fi + +_user_prompt_template=' +
\n\n```\n❯ gh run view | cat \nrun or job ID required when not running interactively\n\nUsage: gh run view [
\r\n\r\n```\r\n$ GH_DEBUG=api gh extensions install https://github.com/IvanRibakov/gh-workflow-stats --pin f2286ac --force\r\n* Request at 2024-10-15 13:12:09.106395563 +0200 CEST m=+0.050985944\r\n* Request to https://api.github.com/repos/IvanRibakov/gh-workflow-stats/releases/latest\r\n> GET /repos/IvanRibakov/gh-workflow-stats/releases/latest HTTP/1.1\r\n> Host: api.github.com\r\n> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview\r\n> Authorization: token ████████████████████\r\n> Content-Type: application/json; charset=utf-8\r\n> Time-Zone: Europe/Madrid\r\n> User-Agent: GitHub CLI 2.54.0\r\n> X-Gh-Cache-Ttl: 30s\r\n\r\n⢿< HTTP/2.0 404 Not Found\r\n< Access-Control-Allow-Origin: *\r\n< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset\r\n< Content-Security-Policy: default-src 'none'\r\n< Content-Type: application/json; charset=utf-8\r\n< Date: Tue, 15 Oct 2024 11:12:14 GMT\r\n< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin\r\n< Server: github.com\r\n< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload\r\n< Vary: Accept-Encoding, Accept, X-Requested-With\r\n< X-Accepted-Oauth-Scopes: repo\r\n< X-Content-Type-Options: nosniff\r\n< X-Frame-Options: deny\r\n< X-Github-Api-Version-Selected: 2022-11-28\r\n< X-Github-Media-Type: github.v3; param=merge-info-preview.nebula-preview; format=json\r\n< X-Github-Request-Id: 9D5A:37C4BC:D9552DD:DCC2930:670E4E0E\r\n< X-Oauth-Scopes: delete:packages, read:org, repo, workflow, write:packages\r\n< X-Ratelimit-Limit: 5000\r\n< X-Ratelimit-Remaining: 4955\r\n< X-Ratelimit-Reset: 1728991844\r\n< X-Ratelimit-Resource: core\r\n< X-Ratelimit-Used: 45\r\n< X-Xss-Protection: 0\r\n\r\n\r\n{\r\n \"message\": \"Not Found\",\r\n \"documentation_url\": \"https://docs.github.com/rest/releases/releases#get-the-latest-release\",\r\n \"status\": \"404\"\r\n}\r\n\r\n* Request took 5.302112651s\r\n* Request at 2024-10-15 13:12:14.408536288 +0200 CEST m=+5.353126669\r\n* Request to https://api.github.com/repos/IvanRibakov/gh-workflow-stats\r\n> GET /repos/IvanRibakov/gh-workflow-stats HTTP/1.1\r\n> Host: api.github.com\r\n> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview\r\n> Authorization: token ████████████████████\r\n> Content-Type: application/json; charset=utf-8\r\n> Time-Zone: Europe/Madrid\r\n> User-Agent: GitHub CLI 2.54.0\r\n> X-Gh-Cache-Ttl: 30s\r\n\r\n...\r\n\r\n{\r\n ...\r\n \"name\": \"gh-workflow-stats\",\r\n \"full_name\": \"IvanRibakov/gh-workflow-stats\",\r\n \"private\": false,\r\n ...\r\n}\r\n\r\n* Request took 254.775396ms\r\n* Request at 2024-10-15 13:12:14.663345618 +0200 CEST m=+5.607935989\r\n* Request to https://api.github.com/repos/IvanRibakov/gh-workflow-stats/contents/gh-workflow-stats\r\n> GET /repos/IvanRibakov/gh-workflow-stats/contents/gh-workflow-stats HTTP/1.1\r\n> Host: api.github.com\r\n> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview\r\n> Authorization: token ████████████████████\r\n> Content-Type: application/json; charset=utf-8\r\n> Time-Zone: Europe/Madrid\r\n> User-Agent: GitHub CLI 2.54.0\r\n> X-Gh-Cache-Ttl: 30s\r\n\r\n⣾< HTTP/2.0 404 Not Found\r\n< Access-Control-Allow-Origin: *\r\n< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset\r\n< Content-Security-Policy: default-src 'none'\r\n< Content-Type: application/json; charset=utf-8\r\n< Date: Tue, 15 Oct 2024 11:12:14 GMT\r\n< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin\r\n< Server: github.com\r\n< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload\r\n< Vary: Accept-Encoding, Accept, X-Requested-With\r\n< X-Accepted-Oauth-Scopes: \r\n< X-Content-Type-Options: nosniff\r\n< X-Frame-Options: deny\r\n< X-Github-Api-Version-Selected: 2022-11-28\r\n< X-Github-Media-Type: github.v3; param=merge-info-preview.nebula-preview; format=json\r\n< X-Github-Request-Id: 9D5A:37C4BC:D95549D:DCC2AE7:670E4E0E\r\n< X-Oauth-Scopes: delete:packages, read:org, repo, workflow, write:packages\r\n< X-Ratelimit-Limit: 5000\r\n< X-Ratelimit-Remaining: 4953\r\n< X-Ratelimit-Reset: 1728991844\r\n< X-Ratelimit-Resource: core\r\n< X-Ratelimit-Used: 47\r\n< X-Xss-Protection: 0\r\n\r\n{\r\n \"message\": \"Not Found\",\r\n \"documentation_url\": \"https://docs.github.com/rest/repos/contents#get-repository-content\",\r\n \"status\": \"404\"\r\n}\r\n\r\n* Request took 282.202949ms\r\nextension is not installable: missing executable\r\n```\r\n\r\n
\r\ngit_protocol : the protocol to use for git clone and push operations {https | ssh} (default https) |