Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions .github/workflows/add-community-bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,24 @@ Run every check and collect all failures before deciding the outcome.

### 2c. Release artifact

- The download URL must be an HTTPS GitHub release asset URL under the submitted
repository:
- The download URL MUST belong to the submitted repository
(`https://github.com/<owner>/<repo>/...` with the same `<owner>/<repo>` as
the Repository URL). Reject URLs for any other GitHub repository.
- The download URL MUST follow the accepted tag-pinned pattern:
`https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>.zip`.
- Confirm the release exists, its tag corresponds to the submitted version
(`vX.Y.Z` or `X.Y.Z`), and the exact ZIP asset is attached to that release.
- If the download URL path contains `releases/latest/`, reject with an
explanation — this URL is floating and not acceptable. Mark this pinning
check failed and skip the HTTP request for this URL, then continue the
remaining validations.
- The `<tag>` segment in the URL MUST correspond to the submitted version.
Accept `vX.Y.Z`, `X.Y.Z`, and scoped tags whose version suffix matches
(for example `aide-v1.0.0` for version `1.0.0`). Reject a tag whose
embedded semver does not equal the submitted version.
- `sha256` is optional. If the submission includes it, verify it matches the
downloaded archive. Requiring `sha256` on every catalog entry is follow-up
work and MUST NOT fail this check when the field is absent.
- Only after the pinning checks pass: confirm the release exists, the exact ZIP
asset is attached to that release, and the download URL returns HTTP 200.
- Confirm the asset name is versioned and consistent with the submitted bundle
ID and version.

Expand Down
21 changes: 18 additions & 3 deletions .github/workflows/add-community-extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,26 @@ deciding pass/fail:
- Confirm the repository contains a `LICENSE` file

### 2d. Release and download URL validation
- The download URL should follow the pattern
`https://github.com/<owner>/<repo>/archive/refs/tags/v<version>.zip`
- The download URL MUST belong to the submitted repository
(`https://github.com/<owner>/<repo>/...` with the same `<owner>/<repo>` as
the Repository URL). Reject URLs for any other GitHub repository.
- The download URL MUST follow one of the accepted tag-pinned patterns:
`https://github.com/<owner>/<repo>/archive/refs/tags/<tag>.zip`
or
`https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>.zip`
- Verify a GitHub release exists matching the submitted version
- If the download URL path contains `releases/latest/`, reject with an
explanation — this URL is floating and not acceptable. Mark this pinning
check failed and skip the HTTP request for this URL, then continue the
remaining validations.
- The `<tag>` segment in the URL MUST correspond to the submitted version.
Accept `vX.Y.Z`, `X.Y.Z`, and scoped tags whose version suffix matches
(for example `aide-v1.0.0` for version `1.0.0`). Reject a tag whose
embedded semver does not equal the submitted version.
- `sha256` is optional. If the submission includes it, verify it matches the
downloaded archive. Requiring `sha256` on every catalog entry is follow-up
work and MUST NOT fail this check when the field is absent.
- Only after the pinning checks pass: verify a GitHub release exists for that
tag, and that the download URL returns HTTP 200.

### 2e. Submission checklists
- Confirm that all required checkboxes in the Testing Checklist and Submission
Expand Down
21 changes: 18 additions & 3 deletions .github/workflows/add-community-preset.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,26 @@ preset** — not just any file named `README.md`, and not a product/framework pi
`specify preset add ...` command for this preset; otherwise it fails check 2d above.

### 2e. Release and download URL validation
- The download URL should follow the pattern
`https://github.com/<owner>/<repo>/archive/refs/tags/v<version>.zip`
- The download URL MUST belong to the submitted repository
(`https://github.com/<owner>/<repo>/...` with the same `<owner>/<repo>` as
the Repository URL). Reject URLs for any other GitHub repository.
- The download URL MUST follow one of the accepted tag-pinned patterns:
`https://github.com/<owner>/<repo>/archive/refs/tags/<tag>.zip`
or
`https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>.zip`
- Verify a GitHub release exists matching the submitted version
- If the download URL path contains `releases/latest/`, reject with an
explanation — this URL is floating and not acceptable. Mark this pinning
check failed and skip the HTTP request for this URL, then continue the
remaining validations.
- The `<tag>` segment in the URL MUST correspond to the submitted version.
Accept `vX.Y.Z`, `X.Y.Z`, and scoped tags whose version suffix matches
(for example `aide-v1.0.0` for version `1.0.0`). Reject a tag whose
embedded semver does not equal the submitted version.
- `sha256` is optional. If the submission includes it, verify it matches the
downloaded archive. Requiring `sha256` on every catalog entry is follow-up
work and MUST NOT fail this check when the field is absent.
- Only after the pinning checks pass: verify a GitHub release exists for that
tag, and that the download URL returns HTTP 200.

### 2f. Submission checklists
- Confirm that all required checkboxes in the Testing Checklist and Submission
Expand Down
56 changes: 56 additions & 0 deletions tests/test_github_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,62 @@ def test_community_submission_automation_is_wired_to_allowed_files():
assert label in assignment_text


# Full clauses from the catalog download-URL checks (issue #4185). Assert the
# complete sentences so independent keywords cannot drift apart.
_CATALOG_DOWNLOAD_URL_CLAUSES = (
(
"The download URL MUST belong to the submitted repository\n"
" (`https://github.com/<owner>/<repo>/...` with the same `<owner>/<repo>` as\n"
" the Repository URL). Reject URLs for any other GitHub repository."
),
(
"If the download URL path contains `releases/latest/`, reject with an\n"
" explanation — this URL is floating and not acceptable. Mark this pinning\n"
" check failed and skip the HTTP request for this URL, then continue the\n"
" remaining validations."
),
(
"The `<tag>` segment in the URL MUST correspond to the submitted version.\n"
" Accept `vX.Y.Z`, `X.Y.Z`, and scoped tags whose version suffix matches\n"
" (for example `aide-v1.0.0` for version `1.0.0`). Reject a tag whose\n"
" embedded semver does not equal the submitted version."
),
(
"`sha256` is optional. If the submission includes it, verify it matches the\n"
" downloaded archive. Requiring `sha256` on every catalog entry is follow-up\n"
" work and MUST NOT fail this check when the field is absent."
),
)


def test_community_submission_workflows_require_tag_pinned_download_urls():
"""Catalog agents must reject floating releases/latest URLs (issue #4185)."""
for workflow, *_ in COMMUNITY_SUBMISSION_WORKFLOWS:
source_text = (WORKFLOWS_DIR / f"add-community-{workflow}.md").read_text(
encoding="utf-8"
)

assert "should follow the pattern" not in source_text.lower()
for clause in _CATALOG_DOWNLOAD_URL_CLAUSES:
assert clause in source_text, f"missing clause in {workflow}: {clause!r}"

if workflow == "bundle":
assert (
"`https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>.zip`."
in source_text
)
assert "archive/refs/tags/" not in source_text
else:
assert (
"`https://github.com/<owner>/<repo>/archive/refs/tags/<tag>.zip`"
in source_text
)
assert (
"`https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>.zip`"
in source_text
)


def test_community_submission_allowed_files_do_not_include_other_catalogs_or_docs():
allowed_by_workflow = {
workflow: set(
Expand Down