Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .specsync/change-sequence.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schema_version": 1,
"sequence": 4,
"id": "CHG-0004-release-v1-7-1-bump-version-files-and-changelog",
"sequence": 5,
"id": "CHG-0005-fix-parse-source-ref-rejecting-refs-containing-a-slash",
"acknowledged_collisions": []
}
1,946 changes: 1,946 additions & 0 deletions .specsync/changes/CHG-0001-adopt-trust-1-and-specsync-5/approvals.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"canonical_applied": true,
"base_commit": "02550b3fac7e9a5ca12f5aecc1773f89972820f0",
"created_at": 1783831307,
"updated_at": 1785179526,
"updated_at": 1785182584,
"affected_specs": [],
"affected_paths": [
".github/workflows/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@
}
],
"requirement_ids": []
},
{
"timestamp": 1785182571,
"commit": "13855367dfb7a019425bdb063f42712d0e79ecd5",
"contract_digest": "208e03c9071862f56bcfc3adb222fd99efcc984021c9da355be0f0734f40e594",
"workspace_digest": "8d532fd0f8512b5969beeb93b425fa767ee38b9d04dae6ce409e1cda33606b20",
"passed": true,
"commands": [
{
"command": "fledge lanes run verify-native",
"success": true,
"exit_code": 0
}
],
"requirement_ids": []
}
]
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"timestamp": 1785179518,
"commit": "809093c87ade08a345d9ae4b4093701df5670041",
"timestamp": 1785182571,
"commit": "13855367dfb7a019425bdb063f42712d0e79ecd5",
"contract_digest": "208e03c9071862f56bcfc3adb222fd99efcc984021c9da355be0f0734f40e594",
"workspace_digest": "ceece6b782680726d37aac4af95e2cdf01e311d9f71a599dd22ca75a92aaac65",
"acceptance_input_digest": "0152352b8b1bd24b999d7b195b8c7e1698d9e61c0ca1a920d3ff7ad7b4aa47e3",
"workspace_digest": "8d532fd0f8512b5969beeb93b425fa767ee38b9d04dae6ce409e1cda33606b20",
"acceptance_input_digest": "0478e021408775dcf5ff95bd9e4b0744709dc9199ead54a5fa11c29a218b055b",
"acceptance_manifest": {
"schema_version": 1,
"entries": [
Expand Down Expand Up @@ -1601,8 +1601,8 @@
"path": "specs/trust/requirements.md",
"kind": "file",
"mode": 33188,
"payload_digest": "2910c102ecd6a637066f6672f2589674c804872c7a67a45b0538ed6590710637",
"entry_digest": "1671bb14e882785d142b4047c90666306f8dae6c931f672ed1fd9a51ed1fd9b4",
"payload_digest": "316c4d681296a7c1060ea188fe12c8ce88395d67c4c60ea2ec19580bf8ab1405",
"entry_digest": "4aed9e3bfefe3b31711f763ae54c52d4982013e56084159ab5ddf18c26bf86b3",
"owners": [
"@exact:delivery"
]
Expand Down Expand Up @@ -1631,8 +1631,8 @@
"path": "specs/trust/trust.spec.md",
"kind": "file",
"mode": 33188,
"payload_digest": "1af69a3349996b41758c67a7b69e6d816af5f94afeb50c81c385cda27f313131",
"entry_digest": "1454439591d65fd53ae64f54157f52005e4b0afa36665ca019bfedd26c8d6420",
"payload_digest": "b0bfb2c3df63a8fe8c7033b7a3a683911baf4bbf426aed42315c7819a4d698b6",
"entry_digest": "a23dc379c70594881ff3ad525ed827aa622b1829c5ec5f8f6f1dca1c211b50a8",
"owners": [
"@exact:delivery"
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"approvals": [
{
"gate": "definition",
"actor": "0xLeif",
"timestamp": 1785182316,
"digest": "aa3f539d30392c13a641e1f7dedfbd91cf857ea276387ba8bd83709e3cd1936e",
"note": null
},
{
"gate": "definition",
"actor": "0xLeif",
"timestamp": 1785182462,
"digest": "4af0a4d9c1f9cbf1a779b95280bb594ca6557c4112b592c8747dbc2ecd9d86d8",
"note": null
},
{
"gate": "acceptance",
"actor": "0xLeif",
"timestamp": 1785182504,
"digest": "534c850edd26ee722ba8d8cf5778b199631dc601c2e932e0d46f91ed89472e2a",
"note": null
}
],
"reopenings": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
id: CHG-0005-fix-parse-source-ref-rejecting-refs-containing-a-slash
state: accepted
type: bug_fix
base_commit: 13855367dfb7a019425bdb063f42712d0e79ecd5
---

# Fix parse_source_ref rejecting refs containing a slash

## Intent

Fix parse_source_ref rejecting refs containing a slash

## Affected Canonical Specs

- `trust`

## Acceptance Criteria

- parse_source_ref splits a trailing @ref containing '/' (e.g. a branch name) into base and ref for both bare owner/repo and full URL forms, including credentialed URLs; credential URLs with no ref still refuse to split

## No-spec Rationale

Not applicable
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
change: CHG-0005-fix-parse-source-ref-rejecting-refs-containing-a-slash
artifact: context
---

# Context

`parse_source_ref` splits a `source@ref` string into a base and an optional git ref, but
`src/trust.rs` refused the split whenever the ref portion contained `/`. This guard exists
to avoid misparsing a credential URL (`https://user:pass@host/path`) as `repo` + ref
`host/path`, but it overshoots: it also rejects legitimate branch refs containing `/`,
which is a common naming convention (`chore/...`, `feature/...`, `docs/...`). A source like
`owner/repo@chore/0.2.0-launch-prep` was left unsplit, and the whole string — ref included
— got globbed into the clone URL, producing a malformed URL and a failed install.

The fix distinguishes the two cases by position rather than by whether the ref contains a
slash: a credential `@` always sits inside the URL's authority component (before any path
separator following the scheme); a trailing ref `@` sits after the full base is already
formed. Checking for a `/` between the scheme and the split point (rather than in the ref
itself) correctly rejects only genuine credential URLs while accepting slash-containing
refs everywhere else, including full clone URLs with embedded credentials plus a trailing
ref.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
change: CHG-0005-fix-parse-source-ref-rejecting-refs-containing-a-slash
module: trust
---

## MODIFIED

### REQUIREMENT REQ-trust-006

The implementation SHALL meet this contract: `parse_source_ref` splits `source@ref` without false-splitting on credential `@` signs, and splits a trailing `@ref` even when the ref itself contains `/` (e.g. a branch name like `chore/0.2.0-launch-prep`)

Acceptance Criteria
- `parse_source_ref("someone/rune@chore/0.2.0-launch-prep")` returns `("someone/rune", Some("chore/0.2.0-launch-prep"))`.
- `parse_source_ref("https://user:token@github.com/owner/repo.git@feature/thing")` returns `("https://user:token@github.com/owner/repo.git", Some("feature/thing"))`.
- `parse_source_ref("https://user:token@github.com/owner/repo.git")` (no trailing ref) still returns `(..., None)` — the credential-URL guard still applies when there is no ref suffix.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"schema_version": 1,
"id": "CHG-0005-fix-parse-source-ref-rejecting-refs-containing-a-slash",
"slug": "fix-parse-source-ref-rejecting-refs-containing-a-slash",
"title": "Fix parse_source_ref rejecting refs containing a slash",
"description": "Fix parse_source_ref rejecting refs containing a slash",
"kind": "bug_fix",
"state": "accepted",
"canonical_applied": true,
"base_commit": "13855367dfb7a019425bdb063f42712d0e79ecd5",
"created_at": 1785182132,
"updated_at": 1785182504,
"affected_specs": [
"trust"
],
"affected_paths": [
"src/trust.rs",
"specs/trust/trust.spec.md",
".specsync/change-sequence.json"
],
"no_spec_change": false,
"no_spec_change_rationale": null,
"acceptance_criteria": [
"parse_source_ref splits a trailing @ref containing '/' (e.g. a branch name) into base and ref for both bare owner/repo and full URL forms, including credentialed URLs; credential URLs with no ref still refuse to split"
],
"selected_artifacts": [
"context",
"testing",
"tasks"
],
"dependencies": [],
"answers": {
"architecture_risk": "no",
"public_contract": "no"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
change: CHG-0005-fix-parse-source-ref-rejecting-refs-containing-a-slash
artifact: tasks
---

# Tasks

- [x] Reproduce: `parse_source_ref("owner/rune@chore/0.2.0-launch-prep")` returns the whole string unsplit
- [x] Rewrite the credential-URL guard to check `@` position (inside the authority) instead of whether the ref contains `/`
- [x] Add tests for slash-containing refs (bare shorthand, full URL, and credentialed URL + trailing ref)
- [x] Update `specs/trust/trust.spec.md` (invariant 5 rewording, invariant 12, behavioral examples, changelog, version bump)
- [x] `cargo test`, `cargo fmt --check`, `cargo clippy -- -D warnings`
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
change: CHG-0005-fix-parse-source-ref-rejecting-refs-containing-a-slash
artifact: testing
---

# Testing

## REQ-trust-006: `parse_source_ref` splits `source@ref` without false-splitting on credential `@` signs, including refs containing `/`

- Automated: `src/trust.rs::tests::parse_source_ref_branch_with_slash` — `"someone/rune@chore/0.2.0-launch-prep"` splits into base `"someone/rune"` and ref `Some("chore/0.2.0-launch-prep")`
- Automated: `src/trust.rs::tests::parse_source_ref_full_url_branch_with_slash` — same, for a full `https://github.com/...` clone URL
- Automated: `src/trust.rs::tests::parse_source_ref_credential_url_with_branch_ref` — a credentialed URL (`https://user:token@...`) plus a trailing slash-containing ref still splits correctly, keeping credentials in the base
- Automated: `src/trust.rs::tests::parse_source_ref_credential_url_no_split` (existing, unchanged) — a credential URL with no ref suffix still returns `None` for the ref, the regression guard for the bug this check protects against

## Regression coverage

- `cargo test`, `cargo fmt --check`, `cargo clippy -- -D warnings` all pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"schema_version": 1,
"attempts": [
{
"timestamp": 1785182396,
"commit": "13855367dfb7a019425bdb063f42712d0e79ecd5",
"contract_digest": "aa3f539d30392c13a641e1f7dedfbd91cf857ea276387ba8bd83709e3cd1936e",
"workspace_digest": "b07e80a6ec95b2d2861b23734aa09e64041d1d78537b69b1c6f4dc8b778d496e",
"passed": false,
"commands": [
{
"command": "fledge lanes run verify-native",
"success": true,
"exit_code": 0
}
],
"requirement_ids": [
"REQ-trust-006"
]
},
{
"timestamp": 1785182493,
"commit": "13855367dfb7a019425bdb063f42712d0e79ecd5",
"contract_digest": "4af0a4d9c1f9cbf1a779b95280bb594ca6557c4112b592c8747dbc2ecd9d86d8",
"workspace_digest": "b07e80a6ec95b2d2861b23734aa09e64041d1d78537b69b1c6f4dc8b778d496e",
"passed": true,
"commands": [
{
"command": "fledge lanes run verify-native",
"success": true,
"exit_code": 0
}
],
"requirement_ids": [
"REQ-trust-006"
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"timestamp": 1785182493,
"commit": "13855367dfb7a019425bdb063f42712d0e79ecd5",
"contract_digest": "4af0a4d9c1f9cbf1a779b95280bb594ca6557c4112b592c8747dbc2ecd9d86d8",
"workspace_digest": "b07e80a6ec95b2d2861b23734aa09e64041d1d78537b69b1c6f4dc8b778d496e",
"acceptance_input_digest": "265f5c6f62ec91ea209b2976b36a2f222c72d0f09798fe67efdac32d0c93c658",
"acceptance_manifest": {
"schema_version": 1,
"entries": [
{
"path": ".specsync/change-sequence.json",
"kind": "file",
"mode": 33188,
"payload_digest": "aef6aedd020464a720cb274856ea6ce7fca949b4f0a94b6ab8535d6399ba96fc",
"entry_digest": "b08711eda8d24a1b740d2f793bd5e478e66d233bbaebae661a098e296357fa70",
"owners": [
"@exact:delivery"
]
},
{
"path": "specs/trust/context.md",
"kind": "file",
"mode": 33188,
"payload_digest": "367ac20cafdbcbcb269d2451b0f61a111c0d4a981c1ec927e81e50c9127a0d34",
"entry_digest": "1865bf2510cd6eae97e32f7246094867646f79ac1c4d7c454493d1579983010c",
"owners": [
"trust"
]
},
{
"path": "specs/trust/requirements.md",
"kind": "file",
"mode": 33188,
"payload_digest": "316c4d681296a7c1060ea188fe12c8ce88395d67c4c60ea2ec19580bf8ab1405",
"entry_digest": "4aed9e3bfefe3b31711f763ae54c52d4982013e56084159ab5ddf18c26bf86b3",
"owners": [
"trust"
]
},
{
"path": "specs/trust/tasks.md",
"kind": "file",
"mode": 33188,
"payload_digest": "5af1c9004df2a7fb4c8d8b160f15ace373406d1e507745e3623db0beec195501",
"entry_digest": "c1ecb82a9794fd9449c22cfdc3f28af54c3cebb2e24db35cf2aa6c8cd78e69dc",
"owners": [
"trust"
]
},
{
"path": "specs/trust/testing.md",
"kind": "file",
"mode": 33188,
"payload_digest": "ec50854aaea8028888a176f7c2b4ada9b77a4468c037e7225ffc847d612d9116",
"entry_digest": "68c9e5537f8153ab540996897ebe4eeb0705166f12b247f18994ae5e3bc4017c",
"owners": [
"trust"
]
},
{
"path": "specs/trust/trust.spec.md",
"kind": "file",
"mode": 33188,
"payload_digest": "b0bfb2c3df63a8fe8c7033b7a3a683911baf4bbf426aed42315c7819a4d698b6",
"entry_digest": "a23dc379c70594881ff3ad525ed827aa622b1829c5ec5f8f6f1dca1c211b50a8",
"owners": [
"trust"
]
},
{
"path": "src/trust.rs",
"kind": "file",
"mode": 33188,
"payload_digest": "ebd08c85f39f0db328b4e4c035b980ef187cd3bf0137c3d2a76207e5f380ad29",
"entry_digest": "613507470eadd078f8d8e229b5161414fe671f5366f8a99a8dccd8d36aa3dd39",
"owners": [
"trust"
]
}
]
},
"passed": true,
"commands": [
{
"command": "fledge lanes run verify-native",
"success": true,
"exit_code": 0
}
],
"requirement_ids": [
"REQ-trust-006"
]
}
7 changes: 6 additions & 1 deletion specs/trust/requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ The implementation SHALL meet this contract: Supports local paths, HTTPS URLs, S

### REQ-trust-006

The implementation SHALL meet this contract: `parse_source_ref` splits `source@ref` without false-splitting on credential `@` signs
The implementation SHALL meet this contract: `parse_source_ref` splits `source@ref` without false-splitting on credential `@` signs, and splits a trailing `@ref` even when the ref itself contains `/` (e.g. a branch name like `chore/0.2.0-launch-prep`)

Acceptance Criteria
- `parse_source_ref("someone/rune@chore/0.2.0-launch-prep")` returns `("someone/rune", Some("chore/0.2.0-launch-prep"))`.
- `parse_source_ref("https://user:token@github.com/owner/repo.git@feature/thing")` returns `("https://user:token@github.com/owner/repo.git", Some("feature/thing"))`.
- `parse_source_ref("https://user:token@github.com/owner/repo.git")` (no trailing ref) still returns `(..., None)` — the credential-URL guard still applies when there is no ref suffix.

### REQ-trust-007

Expand Down
Loading
Loading