fix(git): parse GitHub remotes with embedded http(s) credentials - #260

Merged
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds
Apr 22, 2026
Merged

fix(git): parse GitHub remotes with embedded http(s) credentials#260
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds

Conversation

@sachiniyer

@sachiniyersachiniyer commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

parse_github_remote_url used strict strip_prefix matches against https://github.com/. Valid remotes of the form https://token@github.com/owner/repo.git or https://user:pass@github.com/owner/repo.git — which git itself accepts and stores — fell through and returned None, breaking repo auto-inference for anyone using a credential-embedded remote.

Normalize http(s) URLs by stripping the user[:pass]@ portion of the authority before the prefix check. The strip is scoped to characters before the first /, so a literal @ in the path (e.g. .../repo@tag) is left alone. SSH forms (git@github.com:…, ssh://git@github.com/…) are unaffected.

Test plan

  • cargo test --lib utils::git — 20 pass, including 5 new cases (token creds, user:pass creds, http scheme, @ in path, non-github host with creds)
  • cargo clippy -- -D warnings clean
  • cargo fmt --check clean

Fixes#250.

🤖 Generated with Claude Code


Open in Devin Review

`parse_github_remote_url` relied on exact prefix matching against
`https://github.com/` and friends. Git-stored remotes of the form
`https://token@github.com/owner/repo.git` or
`https://user:pass@github.com/owner/repo.git` — which git itself accepts —
failed that match and returned `None`, so repo auto-inference broke for
anyone using a credential-embedded remote.
Normalize http(s) URLs by stripping the `user[:pass]@` credentials from
the authority before the prefix check. SSH forms are unaffected. The
credential-stripping is scoped to the authority (everything up to the
first `/`) so a literal `@` in the path is preserved.
Fixes#250.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@devin-ai-integrationdevin-ai-integrationBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment threadsrc/utils/git.rs
Comment on lines +39 to +41
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {
return Some(format!("{scheme}{host}/{path}"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Trailing slash added when URL has no path after authority

When the input URL has no path (e.g. https://token@github.com), rest.split_once('/').unwrap_or((rest, "")) sets path to "", and the format string "{scheme}{host}/{path}" produces "https://github.com/" — inserting a / that wasn't in the original. This is harmless here because such a URL would never contain an owner/repo path, so parse_github_remote_url correctly returns None regardless. But worth noting if strip_http_credentials is ever reused in a context where the trailing slash matters.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment threadsrc/utils/git.rs
// (or end of string). Anything after the authority — including a
// `@` in the path — stays untouched.
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: split_once('@') is correct per RFC 3986 for userinfo delimiter

The use of split_once('@') to separate credentials from the host in the authority component is correct per RFC 3986, which defines the userinfo as everything before the first@. A literal @ in a password must be percent-encoded as %40. This means a malformed URL like https://user:p@ss@github.com/owner/repo (unencoded @ in password) would extract host = "ss@github.com" and fail prefix matching — this is the correct behavior for a non-conformant URL.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sachiniyer
sachiniyer merged commit fb56132 into mainApr 22, 2026
13 checks passed
@sachiniyer
sachiniyer deleted the siyer/fix-parse-github-remote-creds branch April 22, 2026 06:31
sachiniyer added a commit that referenced this pull request Apr 22, 2026
)
## Summary
Inputs like `' usedetail/cli'` or `'usedetail / cli'` passed
`validate_owner_repo_format` (which used `.trim().is_empty()` only for
emptiness checks) but were then compared verbatim against `r.full_name`.
The exact match failed and users got a misleading **"Repository not
found. Make sure you have access …"** error for what was actually a
whitespace typo.
Trim the identifier, and trim each half of an `owner/repo` pair, before
matching. The "not found" error now quotes the normalized form so the
hint itself is accurate.
## Stacked on
**#260** (fix for GitHub remote parsing with embedded credentials). Base
branch is `siyer/fix-parse-github-remote-creds`; the diff against main
is just the repos.rs change.
## Test plan
- [x] `cargo test --lib utils::repos` — 22 pass, including 4 new cases
(leading/trailing whitespace, whitespace around slash, bare-name trim,
normalized error message)
- [x] `cargo clippy -- -D warnings` clean
- [x] `cargo fmt --check` clean
Fixes#230.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/261"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sachiniyersachiniyer mentioned this pull request Apr 22, 2026
2 tasks
sachiniyer added a commit that referenced this pull request Apr 23, 2026
## Summary
Patch release rolling up the seven fixes merged since 0.2.1:
- fix(bugs): print empty-results hint when `--vulns` alone yields no
matches (#264)
- fix(datetime): floor sub-second negative timestamps instead of
snapping to epoch (#262)
- chore(deps): bump rustls-webpki to 0.103.13 for RUSTSEC-2026-0104
(#263)
- fix(repos): normalize whitespace in repo identifiers before lookup
(#261)
- fix(git): parse GitHub remotes with embedded http(s) credentials
(#260)
- fix(auth): redirect browser and surface OAuth errors on PKCE callback
failure (#258)
- refactor(config): rewrite `update_config` through the locked handle
(#257)
On merge, the release workflow will tag `v0.2.2` and publish platform
artifacts via cargo-dist.
## Test plan
- [x] `cargo build` succeeds with version 0.2.2
- [ ] Tag `v0.2.2` is created on merge and release workflow publishes
artifacts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/265"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] CLI: GitHub repo inference fails for HTTPS remotes with embedded credentials

1 participant

@sachiniyer
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(git): parse GitHub remotes with embedded http(s) credentials - #260

Merged
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds
Apr 22, 2026
Merged

fix(git): parse GitHub remotes with embedded http(s) credentials#260
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds

Conversation

@sachiniyer

@sachiniyersachiniyer commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

parse_github_remote_url used strict strip_prefix matches against https://github.com/. Valid remotes of the form https://token@github.com/owner/repo.git or https://user:pass@github.com/owner/repo.git — which git itself accepts and stores — fell through and returned None, breaking repo auto-inference for anyone using a credential-embedded remote.

Normalize http(s) URLs by stripping the user[:pass]@ portion of the authority before the prefix check. The strip is scoped to characters before the first /, so a literal @ in the path (e.g. .../repo@tag) is left alone. SSH forms (git@github.com:…, ssh://git@github.com/…) are unaffected.

Test plan

  • cargo test --lib utils::git — 20 pass, including 5 new cases (token creds, user:pass creds, http scheme, @ in path, non-github host with creds)
  • cargo clippy -- -D warnings clean
  • cargo fmt --check clean

Fixes#250.

🤖 Generated with Claude Code


Open in Devin Review

`parse_github_remote_url` relied on exact prefix matching against
`https://github.com/` and friends. Git-stored remotes of the form
`https://token@github.com/owner/repo.git` or
`https://user:pass@github.com/owner/repo.git` — which git itself accepts —
failed that match and returned `None`, so repo auto-inference broke for
anyone using a credential-embedded remote.
Normalize http(s) URLs by stripping the `user[:pass]@` credentials from
the authority before the prefix check. SSH forms are unaffected. The
credential-stripping is scoped to the authority (everything up to the
first `/`) so a literal `@` in the path is preserved.
Fixes#250.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@devin-ai-integrationdevin-ai-integrationBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment threadsrc/utils/git.rs
Comment on lines +39 to +41
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {
return Some(format!("{scheme}{host}/{path}"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Trailing slash added when URL has no path after authority

When the input URL has no path (e.g. https://token@github.com), rest.split_once('/').unwrap_or((rest, "")) sets path to "", and the format string "{scheme}{host}/{path}" produces "https://github.com/" — inserting a / that wasn't in the original. This is harmless here because such a URL would never contain an owner/repo path, so parse_github_remote_url correctly returns None regardless. But worth noting if strip_http_credentials is ever reused in a context where the trailing slash matters.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment threadsrc/utils/git.rs
// (or end of string). Anything after the authority — including a
// `@` in the path — stays untouched.
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: split_once('@') is correct per RFC 3986 for userinfo delimiter

The use of split_once('@') to separate credentials from the host in the authority component is correct per RFC 3986, which defines the userinfo as everything before the first@. A literal @ in a password must be percent-encoded as %40. This means a malformed URL like https://user:p@ss@github.com/owner/repo (unencoded @ in password) would extract host = "ss@github.com" and fail prefix matching — this is the correct behavior for a non-conformant URL.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sachiniyer
sachiniyer merged commit fb56132 into mainApr 22, 2026
13 checks passed
@sachiniyer
sachiniyer deleted the siyer/fix-parse-github-remote-creds branch April 22, 2026 06:31
sachiniyer added a commit that referenced this pull request Apr 22, 2026
)
## Summary
Inputs like `' usedetail/cli'` or `'usedetail / cli'` passed
`validate_owner_repo_format` (which used `.trim().is_empty()` only for
emptiness checks) but were then compared verbatim against `r.full_name`.
The exact match failed and users got a misleading **"Repository not
found. Make sure you have access …"** error for what was actually a
whitespace typo.
Trim the identifier, and trim each half of an `owner/repo` pair, before
matching. The "not found" error now quotes the normalized form so the
hint itself is accurate.
## Stacked on
**#260** (fix for GitHub remote parsing with embedded credentials). Base
branch is `siyer/fix-parse-github-remote-creds`; the diff against main
is just the repos.rs change.
## Test plan
- [x] `cargo test --lib utils::repos` — 22 pass, including 4 new cases
(leading/trailing whitespace, whitespace around slash, bare-name trim,
normalized error message)
- [x] `cargo clippy -- -D warnings` clean
- [x] `cargo fmt --check` clean
Fixes#230.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/261"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sachiniyersachiniyer mentioned this pull request Apr 22, 2026
2 tasks
sachiniyer added a commit that referenced this pull request Apr 23, 2026
## Summary
Patch release rolling up the seven fixes merged since 0.2.1:
- fix(bugs): print empty-results hint when `--vulns` alone yields no
matches (#264)
- fix(datetime): floor sub-second negative timestamps instead of
snapping to epoch (#262)
- chore(deps): bump rustls-webpki to 0.103.13 for RUSTSEC-2026-0104
(#263)
- fix(repos): normalize whitespace in repo identifiers before lookup
(#261)
- fix(git): parse GitHub remotes with embedded http(s) credentials
(#260)
- fix(auth): redirect browser and surface OAuth errors on PKCE callback
failure (#258)
- refactor(config): rewrite `update_config` through the locked handle
(#257)
On merge, the release workflow will tag `v0.2.2` and publish platform
artifacts via cargo-dist.
## Test plan
- [x] `cargo build` succeeds with version 0.2.2
- [ ] Tag `v0.2.2` is created on merge and release workflow publishes
artifacts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/265"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] CLI: GitHub repo inference fails for HTTPS remotes with embedded credentials

1 participant

@sachiniyer
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(git): parse GitHub remotes with embedded http(s) credentials - #260

Merged
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds
Apr 22, 2026
Merged

fix(git): parse GitHub remotes with embedded http(s) credentials#260
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds

Conversation

@sachiniyer

@sachiniyersachiniyer commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

parse_github_remote_url used strict strip_prefix matches against https://github.com/. Valid remotes of the form https://token@github.com/owner/repo.git or https://user:pass@github.com/owner/repo.git — which git itself accepts and stores — fell through and returned None, breaking repo auto-inference for anyone using a credential-embedded remote.

Normalize http(s) URLs by stripping the user[:pass]@ portion of the authority before the prefix check. The strip is scoped to characters before the first /, so a literal @ in the path (e.g. .../repo@tag) is left alone. SSH forms (git@github.com:…, ssh://git@github.com/…) are unaffected.

Test plan

  • cargo test --lib utils::git — 20 pass, including 5 new cases (token creds, user:pass creds, http scheme, @ in path, non-github host with creds)
  • cargo clippy -- -D warnings clean
  • cargo fmt --check clean

Fixes#250.

🤖 Generated with Claude Code


Open in Devin Review

`parse_github_remote_url` relied on exact prefix matching against
`https://github.com/` and friends. Git-stored remotes of the form
`https://token@github.com/owner/repo.git` or
`https://user:pass@github.com/owner/repo.git` — which git itself accepts —
failed that match and returned `None`, so repo auto-inference broke for
anyone using a credential-embedded remote.
Normalize http(s) URLs by stripping the `user[:pass]@` credentials from
the authority before the prefix check. SSH forms are unaffected. The
credential-stripping is scoped to the authority (everything up to the
first `/`) so a literal `@` in the path is preserved.
Fixes#250.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@devin-ai-integrationdevin-ai-integrationBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment threadsrc/utils/git.rs
Comment on lines +39 to +41
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {
return Some(format!("{scheme}{host}/{path}"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Trailing slash added when URL has no path after authority

When the input URL has no path (e.g. https://token@github.com), rest.split_once('/').unwrap_or((rest, "")) sets path to "", and the format string "{scheme}{host}/{path}" produces "https://github.com/" — inserting a / that wasn't in the original. This is harmless here because such a URL would never contain an owner/repo path, so parse_github_remote_url correctly returns None regardless. But worth noting if strip_http_credentials is ever reused in a context where the trailing slash matters.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment threadsrc/utils/git.rs
// (or end of string). Anything after the authority — including a
// `@` in the path — stays untouched.
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: split_once('@') is correct per RFC 3986 for userinfo delimiter

The use of split_once('@') to separate credentials from the host in the authority component is correct per RFC 3986, which defines the userinfo as everything before the first@. A literal @ in a password must be percent-encoded as %40. This means a malformed URL like https://user:p@ss@github.com/owner/repo (unencoded @ in password) would extract host = "ss@github.com" and fail prefix matching — this is the correct behavior for a non-conformant URL.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sachiniyer
sachiniyer merged commit fb56132 into mainApr 22, 2026
13 checks passed
@sachiniyer
sachiniyer deleted the siyer/fix-parse-github-remote-creds branch April 22, 2026 06:31
sachiniyer added a commit that referenced this pull request Apr 22, 2026
)
## Summary
Inputs like `' usedetail/cli'` or `'usedetail / cli'` passed
`validate_owner_repo_format` (which used `.trim().is_empty()` only for
emptiness checks) but were then compared verbatim against `r.full_name`.
The exact match failed and users got a misleading **"Repository not
found. Make sure you have access …"** error for what was actually a
whitespace typo.
Trim the identifier, and trim each half of an `owner/repo` pair, before
matching. The "not found" error now quotes the normalized form so the
hint itself is accurate.
## Stacked on
**#260** (fix for GitHub remote parsing with embedded credentials). Base
branch is `siyer/fix-parse-github-remote-creds`; the diff against main
is just the repos.rs change.
## Test plan
- [x] `cargo test --lib utils::repos` — 22 pass, including 4 new cases
(leading/trailing whitespace, whitespace around slash, bare-name trim,
normalized error message)
- [x] `cargo clippy -- -D warnings` clean
- [x] `cargo fmt --check` clean
Fixes#230.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/261"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sachiniyersachiniyer mentioned this pull request Apr 22, 2026
2 tasks
sachiniyer added a commit that referenced this pull request Apr 23, 2026
## Summary
Patch release rolling up the seven fixes merged since 0.2.1:
- fix(bugs): print empty-results hint when `--vulns` alone yields no
matches (#264)
- fix(datetime): floor sub-second negative timestamps instead of
snapping to epoch (#262)
- chore(deps): bump rustls-webpki to 0.103.13 for RUSTSEC-2026-0104
(#263)
- fix(repos): normalize whitespace in repo identifiers before lookup
(#261)
- fix(git): parse GitHub remotes with embedded http(s) credentials
(#260)
- fix(auth): redirect browser and surface OAuth errors on PKCE callback
failure (#258)
- refactor(config): rewrite `update_config` through the locked handle
(#257)
On merge, the release workflow will tag `v0.2.2` and publish platform
artifacts via cargo-dist.
## Test plan
- [x] `cargo build` succeeds with version 0.2.2
- [ ] Tag `v0.2.2` is created on merge and release workflow publishes
artifacts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/265"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] CLI: GitHub repo inference fails for HTTPS remotes with embedded credentials

1 participant

@sachiniyer
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(git): parse GitHub remotes with embedded http(s) credentials - #260

Merged
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds
Apr 22, 2026
Merged

fix(git): parse GitHub remotes with embedded http(s) credentials#260
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds

Conversation

@sachiniyer

@sachiniyersachiniyer commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

parse_github_remote_url used strict strip_prefix matches against https://github.com/. Valid remotes of the form https://token@github.com/owner/repo.git or https://user:pass@github.com/owner/repo.git — which git itself accepts and stores — fell through and returned None, breaking repo auto-inference for anyone using a credential-embedded remote.

Normalize http(s) URLs by stripping the user[:pass]@ portion of the authority before the prefix check. The strip is scoped to characters before the first /, so a literal @ in the path (e.g. .../repo@tag) is left alone. SSH forms (git@github.com:…, ssh://git@github.com/…) are unaffected.

Test plan

  • cargo test --lib utils::git — 20 pass, including 5 new cases (token creds, user:pass creds, http scheme, @ in path, non-github host with creds)
  • cargo clippy -- -D warnings clean
  • cargo fmt --check clean

Fixes#250.

🤖 Generated with Claude Code


Open in Devin Review

`parse_github_remote_url` relied on exact prefix matching against
`https://github.com/` and friends. Git-stored remotes of the form
`https://token@github.com/owner/repo.git` or
`https://user:pass@github.com/owner/repo.git` — which git itself accepts —
failed that match and returned `None`, so repo auto-inference broke for
anyone using a credential-embedded remote.
Normalize http(s) URLs by stripping the `user[:pass]@` credentials from
the authority before the prefix check. SSH forms are unaffected. The
credential-stripping is scoped to the authority (everything up to the
first `/`) so a literal `@` in the path is preserved.
Fixes#250.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@devin-ai-integrationdevin-ai-integrationBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment threadsrc/utils/git.rs
Comment on lines +39 to +41
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {
return Some(format!("{scheme}{host}/{path}"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Trailing slash added when URL has no path after authority

When the input URL has no path (e.g. https://token@github.com), rest.split_once('/').unwrap_or((rest, "")) sets path to "", and the format string "{scheme}{host}/{path}" produces "https://github.com/" — inserting a / that wasn't in the original. This is harmless here because such a URL would never contain an owner/repo path, so parse_github_remote_url correctly returns None regardless. But worth noting if strip_http_credentials is ever reused in a context where the trailing slash matters.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment threadsrc/utils/git.rs
// (or end of string). Anything after the authority — including a
// `@` in the path — stays untouched.
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: split_once('@') is correct per RFC 3986 for userinfo delimiter

The use of split_once('@') to separate credentials from the host in the authority component is correct per RFC 3986, which defines the userinfo as everything before the first@. A literal @ in a password must be percent-encoded as %40. This means a malformed URL like https://user:p@ss@github.com/owner/repo (unencoded @ in password) would extract host = "ss@github.com" and fail prefix matching — this is the correct behavior for a non-conformant URL.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sachiniyer
sachiniyer merged commit fb56132 into mainApr 22, 2026
13 checks passed
@sachiniyer
sachiniyer deleted the siyer/fix-parse-github-remote-creds branch April 22, 2026 06:31
sachiniyer added a commit that referenced this pull request Apr 22, 2026
)
## Summary
Inputs like `' usedetail/cli'` or `'usedetail / cli'` passed
`validate_owner_repo_format` (which used `.trim().is_empty()` only for
emptiness checks) but were then compared verbatim against `r.full_name`.
The exact match failed and users got a misleading **"Repository not
found. Make sure you have access …"** error for what was actually a
whitespace typo.
Trim the identifier, and trim each half of an `owner/repo` pair, before
matching. The "not found" error now quotes the normalized form so the
hint itself is accurate.
## Stacked on
**#260** (fix for GitHub remote parsing with embedded credentials). Base
branch is `siyer/fix-parse-github-remote-creds`; the diff against main
is just the repos.rs change.
## Test plan
- [x] `cargo test --lib utils::repos` — 22 pass, including 4 new cases
(leading/trailing whitespace, whitespace around slash, bare-name trim,
normalized error message)
- [x] `cargo clippy -- -D warnings` clean
- [x] `cargo fmt --check` clean
Fixes#230.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/261"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sachiniyersachiniyer mentioned this pull request Apr 22, 2026
2 tasks
sachiniyer added a commit that referenced this pull request Apr 23, 2026
## Summary
Patch release rolling up the seven fixes merged since 0.2.1:
- fix(bugs): print empty-results hint when `--vulns` alone yields no
matches (#264)
- fix(datetime): floor sub-second negative timestamps instead of
snapping to epoch (#262)
- chore(deps): bump rustls-webpki to 0.103.13 for RUSTSEC-2026-0104
(#263)
- fix(repos): normalize whitespace in repo identifiers before lookup
(#261)
- fix(git): parse GitHub remotes with embedded http(s) credentials
(#260)
- fix(auth): redirect browser and surface OAuth errors on PKCE callback
failure (#258)
- refactor(config): rewrite `update_config` through the locked handle
(#257)
On merge, the release workflow will tag `v0.2.2` and publish platform
artifacts via cargo-dist.
## Test plan
- [x] `cargo build` succeeds with version 0.2.2
- [ ] Tag `v0.2.2` is created on merge and release workflow publishes
artifacts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/265"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] CLI: GitHub repo inference fails for HTTPS remotes with embedded credentials

1 participant

@sachiniyer
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(git): parse GitHub remotes with embedded http(s) credentials - #260

Merged
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds
Apr 22, 2026
Merged

fix(git): parse GitHub remotes with embedded http(s) credentials#260
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds

Conversation

@sachiniyer

@sachiniyersachiniyer commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

parse_github_remote_url used strict strip_prefix matches against https://github.com/. Valid remotes of the form https://token@github.com/owner/repo.git or https://user:pass@github.com/owner/repo.git — which git itself accepts and stores — fell through and returned None, breaking repo auto-inference for anyone using a credential-embedded remote.

Normalize http(s) URLs by stripping the user[:pass]@ portion of the authority before the prefix check. The strip is scoped to characters before the first /, so a literal @ in the path (e.g. .../repo@tag) is left alone. SSH forms (git@github.com:…, ssh://git@github.com/…) are unaffected.

Test plan

  • cargo test --lib utils::git — 20 pass, including 5 new cases (token creds, user:pass creds, http scheme, @ in path, non-github host with creds)
  • cargo clippy -- -D warnings clean
  • cargo fmt --check clean

Fixes#250.

🤖 Generated with Claude Code


Open in Devin Review

`parse_github_remote_url` relied on exact prefix matching against
`https://github.com/` and friends. Git-stored remotes of the form
`https://token@github.com/owner/repo.git` or
`https://user:pass@github.com/owner/repo.git` — which git itself accepts —
failed that match and returned `None`, so repo auto-inference broke for
anyone using a credential-embedded remote.
Normalize http(s) URLs by stripping the `user[:pass]@` credentials from
the authority before the prefix check. SSH forms are unaffected. The
credential-stripping is scoped to the authority (everything up to the
first `/`) so a literal `@` in the path is preserved.
Fixes#250.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@devin-ai-integrationdevin-ai-integrationBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment threadsrc/utils/git.rs
Comment on lines +39 to +41
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {
return Some(format!("{scheme}{host}/{path}"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Trailing slash added when URL has no path after authority

When the input URL has no path (e.g. https://token@github.com), rest.split_once('/').unwrap_or((rest, "")) sets path to "", and the format string "{scheme}{host}/{path}" produces "https://github.com/" — inserting a / that wasn't in the original. This is harmless here because such a URL would never contain an owner/repo path, so parse_github_remote_url correctly returns None regardless. But worth noting if strip_http_credentials is ever reused in a context where the trailing slash matters.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment threadsrc/utils/git.rs
// (or end of string). Anything after the authority — including a
// `@` in the path — stays untouched.
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: split_once('@') is correct per RFC 3986 for userinfo delimiter

The use of split_once('@') to separate credentials from the host in the authority component is correct per RFC 3986, which defines the userinfo as everything before the first@. A literal @ in a password must be percent-encoded as %40. This means a malformed URL like https://user:p@ss@github.com/owner/repo (unencoded @ in password) would extract host = "ss@github.com" and fail prefix matching — this is the correct behavior for a non-conformant URL.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sachiniyer
sachiniyer merged commit fb56132 into mainApr 22, 2026
13 checks passed
@sachiniyer
sachiniyer deleted the siyer/fix-parse-github-remote-creds branch April 22, 2026 06:31
sachiniyer added a commit that referenced this pull request Apr 22, 2026
)
## Summary
Inputs like `' usedetail/cli'` or `'usedetail / cli'` passed
`validate_owner_repo_format` (which used `.trim().is_empty()` only for
emptiness checks) but were then compared verbatim against `r.full_name`.
The exact match failed and users got a misleading **"Repository not
found. Make sure you have access …"** error for what was actually a
whitespace typo.
Trim the identifier, and trim each half of an `owner/repo` pair, before
matching. The "not found" error now quotes the normalized form so the
hint itself is accurate.
## Stacked on
**#260** (fix for GitHub remote parsing with embedded credentials). Base
branch is `siyer/fix-parse-github-remote-creds`; the diff against main
is just the repos.rs change.
## Test plan
- [x] `cargo test --lib utils::repos` — 22 pass, including 4 new cases
(leading/trailing whitespace, whitespace around slash, bare-name trim,
normalized error message)
- [x] `cargo clippy -- -D warnings` clean
- [x] `cargo fmt --check` clean
Fixes#230.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/261"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sachiniyersachiniyer mentioned this pull request Apr 22, 2026
2 tasks
sachiniyer added a commit that referenced this pull request Apr 23, 2026
## Summary
Patch release rolling up the seven fixes merged since 0.2.1:
- fix(bugs): print empty-results hint when `--vulns` alone yields no
matches (#264)
- fix(datetime): floor sub-second negative timestamps instead of
snapping to epoch (#262)
- chore(deps): bump rustls-webpki to 0.103.13 for RUSTSEC-2026-0104
(#263)
- fix(repos): normalize whitespace in repo identifiers before lookup
(#261)
- fix(git): parse GitHub remotes with embedded http(s) credentials
(#260)
- fix(auth): redirect browser and surface OAuth errors on PKCE callback
failure (#258)
- refactor(config): rewrite `update_config` through the locked handle
(#257)
On merge, the release workflow will tag `v0.2.2` and publish platform
artifacts via cargo-dist.
## Test plan
- [x] `cargo build` succeeds with version 0.2.2
- [ ] Tag `v0.2.2` is created on merge and release workflow publishes
artifacts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/265"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] CLI: GitHub repo inference fails for HTTPS remotes with embedded credentials

1 participant

@sachiniyer
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(git): parse GitHub remotes with embedded http(s) credentials - #260

Merged
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds
Apr 22, 2026
Merged

fix(git): parse GitHub remotes with embedded http(s) credentials#260
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds

Conversation

@sachiniyer

@sachiniyersachiniyer commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

parse_github_remote_url used strict strip_prefix matches against https://github.com/. Valid remotes of the form https://token@github.com/owner/repo.git or https://user:pass@github.com/owner/repo.git — which git itself accepts and stores — fell through and returned None, breaking repo auto-inference for anyone using a credential-embedded remote.

Normalize http(s) URLs by stripping the user[:pass]@ portion of the authority before the prefix check. The strip is scoped to characters before the first /, so a literal @ in the path (e.g. .../repo@tag) is left alone. SSH forms (git@github.com:…, ssh://git@github.com/…) are unaffected.

Test plan

  • cargo test --lib utils::git — 20 pass, including 5 new cases (token creds, user:pass creds, http scheme, @ in path, non-github host with creds)
  • cargo clippy -- -D warnings clean
  • cargo fmt --check clean

Fixes#250.

🤖 Generated with Claude Code


Open in Devin Review

`parse_github_remote_url` relied on exact prefix matching against
`https://github.com/` and friends. Git-stored remotes of the form
`https://token@github.com/owner/repo.git` or
`https://user:pass@github.com/owner/repo.git` — which git itself accepts —
failed that match and returned `None`, so repo auto-inference broke for
anyone using a credential-embedded remote.
Normalize http(s) URLs by stripping the `user[:pass]@` credentials from
the authority before the prefix check. SSH forms are unaffected. The
credential-stripping is scoped to the authority (everything up to the
first `/`) so a literal `@` in the path is preserved.
Fixes#250.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@devin-ai-integrationdevin-ai-integrationBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment threadsrc/utils/git.rs
Comment on lines +39 to +41
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {
return Some(format!("{scheme}{host}/{path}"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Trailing slash added when URL has no path after authority

When the input URL has no path (e.g. https://token@github.com), rest.split_once('/').unwrap_or((rest, "")) sets path to "", and the format string "{scheme}{host}/{path}" produces "https://github.com/" — inserting a / that wasn't in the original. This is harmless here because such a URL would never contain an owner/repo path, so parse_github_remote_url correctly returns None regardless. But worth noting if strip_http_credentials is ever reused in a context where the trailing slash matters.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment threadsrc/utils/git.rs
// (or end of string). Anything after the authority — including a
// `@` in the path — stays untouched.
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: split_once('@') is correct per RFC 3986 for userinfo delimiter

The use of split_once('@') to separate credentials from the host in the authority component is correct per RFC 3986, which defines the userinfo as everything before the first@. A literal @ in a password must be percent-encoded as %40. This means a malformed URL like https://user:p@ss@github.com/owner/repo (unencoded @ in password) would extract host = "ss@github.com" and fail prefix matching — this is the correct behavior for a non-conformant URL.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sachiniyer
sachiniyer merged commit fb56132 into mainApr 22, 2026
13 checks passed
@sachiniyer
sachiniyer deleted the siyer/fix-parse-github-remote-creds branch April 22, 2026 06:31
sachiniyer added a commit that referenced this pull request Apr 22, 2026
)
## Summary
Inputs like `' usedetail/cli'` or `'usedetail / cli'` passed
`validate_owner_repo_format` (which used `.trim().is_empty()` only for
emptiness checks) but were then compared verbatim against `r.full_name`.
The exact match failed and users got a misleading **"Repository not
found. Make sure you have access …"** error for what was actually a
whitespace typo.
Trim the identifier, and trim each half of an `owner/repo` pair, before
matching. The "not found" error now quotes the normalized form so the
hint itself is accurate.
## Stacked on
**#260** (fix for GitHub remote parsing with embedded credentials). Base
branch is `siyer/fix-parse-github-remote-creds`; the diff against main
is just the repos.rs change.
## Test plan
- [x] `cargo test --lib utils::repos` — 22 pass, including 4 new cases
(leading/trailing whitespace, whitespace around slash, bare-name trim,
normalized error message)
- [x] `cargo clippy -- -D warnings` clean
- [x] `cargo fmt --check` clean
Fixes#230.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/261"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sachiniyersachiniyer mentioned this pull request Apr 22, 2026
2 tasks
sachiniyer added a commit that referenced this pull request Apr 23, 2026
## Summary
Patch release rolling up the seven fixes merged since 0.2.1:
- fix(bugs): print empty-results hint when `--vulns` alone yields no
matches (#264)
- fix(datetime): floor sub-second negative timestamps instead of
snapping to epoch (#262)
- chore(deps): bump rustls-webpki to 0.103.13 for RUSTSEC-2026-0104
(#263)
- fix(repos): normalize whitespace in repo identifiers before lookup
(#261)
- fix(git): parse GitHub remotes with embedded http(s) credentials
(#260)
- fix(auth): redirect browser and surface OAuth errors on PKCE callback
failure (#258)
- refactor(config): rewrite `update_config` through the locked handle
(#257)
On merge, the release workflow will tag `v0.2.2` and publish platform
artifacts via cargo-dist.
## Test plan
- [x] `cargo build` succeeds with version 0.2.2
- [ ] Tag `v0.2.2` is created on merge and release workflow publishes
artifacts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/265"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] CLI: GitHub repo inference fails for HTTPS remotes with embedded credentials

1 participant

@sachiniyer
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(git): parse GitHub remotes with embedded http(s) credentials - #260

Merged
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds
Apr 22, 2026
Merged

fix(git): parse GitHub remotes with embedded http(s) credentials#260
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds

Conversation

@sachiniyer

@sachiniyersachiniyer commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

parse_github_remote_url used strict strip_prefix matches against https://github.com/. Valid remotes of the form https://token@github.com/owner/repo.git or https://user:pass@github.com/owner/repo.git — which git itself accepts and stores — fell through and returned None, breaking repo auto-inference for anyone using a credential-embedded remote.

Normalize http(s) URLs by stripping the user[:pass]@ portion of the authority before the prefix check. The strip is scoped to characters before the first /, so a literal @ in the path (e.g. .../repo@tag) is left alone. SSH forms (git@github.com:…, ssh://git@github.com/…) are unaffected.

Test plan

  • cargo test --lib utils::git — 20 pass, including 5 new cases (token creds, user:pass creds, http scheme, @ in path, non-github host with creds)
  • cargo clippy -- -D warnings clean
  • cargo fmt --check clean

Fixes#250.

🤖 Generated with Claude Code


Open in Devin Review

`parse_github_remote_url` relied on exact prefix matching against
`https://github.com/` and friends. Git-stored remotes of the form
`https://token@github.com/owner/repo.git` or
`https://user:pass@github.com/owner/repo.git` — which git itself accepts —
failed that match and returned `None`, so repo auto-inference broke for
anyone using a credential-embedded remote.
Normalize http(s) URLs by stripping the `user[:pass]@` credentials from
the authority before the prefix check. SSH forms are unaffected. The
credential-stripping is scoped to the authority (everything up to the
first `/`) so a literal `@` in the path is preserved.
Fixes#250.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@devin-ai-integrationdevin-ai-integrationBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment threadsrc/utils/git.rs
Comment on lines +39 to +41
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {
return Some(format!("{scheme}{host}/{path}"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Trailing slash added when URL has no path after authority

When the input URL has no path (e.g. https://token@github.com), rest.split_once('/').unwrap_or((rest, "")) sets path to "", and the format string "{scheme}{host}/{path}" produces "https://github.com/" — inserting a / that wasn't in the original. This is harmless here because such a URL would never contain an owner/repo path, so parse_github_remote_url correctly returns None regardless. But worth noting if strip_http_credentials is ever reused in a context where the trailing slash matters.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment threadsrc/utils/git.rs
// (or end of string). Anything after the authority — including a
// `@` in the path — stays untouched.
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: split_once('@') is correct per RFC 3986 for userinfo delimiter

The use of split_once('@') to separate credentials from the host in the authority component is correct per RFC 3986, which defines the userinfo as everything before the first@. A literal @ in a password must be percent-encoded as %40. This means a malformed URL like https://user:p@ss@github.com/owner/repo (unencoded @ in password) would extract host = "ss@github.com" and fail prefix matching — this is the correct behavior for a non-conformant URL.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sachiniyer
sachiniyer merged commit fb56132 into mainApr 22, 2026
13 checks passed
@sachiniyer
sachiniyer deleted the siyer/fix-parse-github-remote-creds branch April 22, 2026 06:31
sachiniyer added a commit that referenced this pull request Apr 22, 2026
)
## Summary
Inputs like `' usedetail/cli'` or `'usedetail / cli'` passed
`validate_owner_repo_format` (which used `.trim().is_empty()` only for
emptiness checks) but were then compared verbatim against `r.full_name`.
The exact match failed and users got a misleading **"Repository not
found. Make sure you have access …"** error for what was actually a
whitespace typo.
Trim the identifier, and trim each half of an `owner/repo` pair, before
matching. The "not found" error now quotes the normalized form so the
hint itself is accurate.
## Stacked on
**#260** (fix for GitHub remote parsing with embedded credentials). Base
branch is `siyer/fix-parse-github-remote-creds`; the diff against main
is just the repos.rs change.
## Test plan
- [x] `cargo test --lib utils::repos` — 22 pass, including 4 new cases
(leading/trailing whitespace, whitespace around slash, bare-name trim,
normalized error message)
- [x] `cargo clippy -- -D warnings` clean
- [x] `cargo fmt --check` clean
Fixes#230.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/261"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sachiniyersachiniyer mentioned this pull request Apr 22, 2026
2 tasks
sachiniyer added a commit that referenced this pull request Apr 23, 2026
## Summary
Patch release rolling up the seven fixes merged since 0.2.1:
- fix(bugs): print empty-results hint when `--vulns` alone yields no
matches (#264)
- fix(datetime): floor sub-second negative timestamps instead of
snapping to epoch (#262)
- chore(deps): bump rustls-webpki to 0.103.13 for RUSTSEC-2026-0104
(#263)
- fix(repos): normalize whitespace in repo identifiers before lookup
(#261)
- fix(git): parse GitHub remotes with embedded http(s) credentials
(#260)
- fix(auth): redirect browser and surface OAuth errors on PKCE callback
failure (#258)
- refactor(config): rewrite `update_config` through the locked handle
(#257)
On merge, the release workflow will tag `v0.2.2` and publish platform
artifacts via cargo-dist.
## Test plan
- [x] `cargo build` succeeds with version 0.2.2
- [ ] Tag `v0.2.2` is created on merge and release workflow publishes
artifacts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/265"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] CLI: GitHub repo inference fails for HTTPS remotes with embedded credentials

1 participant

@sachiniyer
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(git): parse GitHub remotes with embedded http(s) credentials - #260

Merged
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds
Apr 22, 2026
Merged

fix(git): parse GitHub remotes with embedded http(s) credentials#260
sachiniyer merged 1 commit into
mainfrom
siyer/fix-parse-github-remote-creds

Conversation

@sachiniyer

@sachiniyersachiniyer commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

parse_github_remote_url used strict strip_prefix matches against https://github.com/. Valid remotes of the form https://token@github.com/owner/repo.git or https://user:pass@github.com/owner/repo.git — which git itself accepts and stores — fell through and returned None, breaking repo auto-inference for anyone using a credential-embedded remote.

Normalize http(s) URLs by stripping the user[:pass]@ portion of the authority before the prefix check. The strip is scoped to characters before the first /, so a literal @ in the path (e.g. .../repo@tag) is left alone. SSH forms (git@github.com:…, ssh://git@github.com/…) are unaffected.

Test plan

  • cargo test --lib utils::git — 20 pass, including 5 new cases (token creds, user:pass creds, http scheme, @ in path, non-github host with creds)
  • cargo clippy -- -D warnings clean
  • cargo fmt --check clean

Fixes#250.

🤖 Generated with Claude Code


Open in Devin Review

`parse_github_remote_url` relied on exact prefix matching against
`https://github.com/` and friends. Git-stored remotes of the form
`https://token@github.com/owner/repo.git` or
`https://user:pass@github.com/owner/repo.git` — which git itself accepts —
failed that match and returned `None`, so repo auto-inference broke for
anyone using a credential-embedded remote.
Normalize http(s) URLs by stripping the `user[:pass]@` credentials from
the authority before the prefix check. SSH forms are unaffected. The
credential-stripping is scoped to the authority (everything up to the
first `/`) so a literal `@` in the path is preserved.
Fixes#250.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@devin-ai-integrationdevin-ai-integrationBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment threadsrc/utils/git.rs
Comment on lines +39 to +41
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {
return Some(format!("{scheme}{host}/{path}"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Trailing slash added when URL has no path after authority

When the input URL has no path (e.g. https://token@github.com), rest.split_once('/').unwrap_or((rest, "")) sets path to "", and the format string "{scheme}{host}/{path}" produces "https://github.com/" — inserting a / that wasn't in the original. This is harmless here because such a URL would never contain an owner/repo path, so parse_github_remote_url correctly returns None regardless. But worth noting if strip_http_credentials is ever reused in a context where the trailing slash matters.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment threadsrc/utils/git.rs
// (or end of string). Anything after the authority — including a
// `@` in the path — stays untouched.
let (authority, path) = rest.split_once('/').unwrap_or((rest, ""));
if let Some((_, host)) = authority.split_once('@') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: split_once('@') is correct per RFC 3986 for userinfo delimiter

The use of split_once('@') to separate credentials from the host in the authority component is correct per RFC 3986, which defines the userinfo as everything before the first@. A literal @ in a password must be percent-encoded as %40. This means a malformed URL like https://user:p@ss@github.com/owner/repo (unencoded @ in password) would extract host = "ss@github.com" and fail prefix matching — this is the correct behavior for a non-conformant URL.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sachiniyer
sachiniyer merged commit fb56132 into mainApr 22, 2026
13 checks passed
@sachiniyer
sachiniyer deleted the siyer/fix-parse-github-remote-creds branch April 22, 2026 06:31
sachiniyer added a commit that referenced this pull request Apr 22, 2026
)
## Summary
Inputs like `' usedetail/cli'` or `'usedetail / cli'` passed
`validate_owner_repo_format` (which used `.trim().is_empty()` only for
emptiness checks) but were then compared verbatim against `r.full_name`.
The exact match failed and users got a misleading **"Repository not
found. Make sure you have access …"** error for what was actually a
whitespace typo.
Trim the identifier, and trim each half of an `owner/repo` pair, before
matching. The "not found" error now quotes the normalized form so the
hint itself is accurate.
## Stacked on
**#260** (fix for GitHub remote parsing with embedded credentials). Base
branch is `siyer/fix-parse-github-remote-creds`; the diff against main
is just the repos.rs change.
## Test plan
- [x] `cargo test --lib utils::repos` — 22 pass, including 4 new cases
(leading/trailing whitespace, whitespace around slash, bare-name trim,
normalized error message)
- [x] `cargo clippy -- -D warnings` clean
- [x] `cargo fmt --check` clean
Fixes#230.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/261"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sachiniyersachiniyer mentioned this pull request Apr 22, 2026
2 tasks
sachiniyer added a commit that referenced this pull request Apr 23, 2026
## Summary
Patch release rolling up the seven fixes merged since 0.2.1:
- fix(bugs): print empty-results hint when `--vulns` alone yields no
matches (#264)
- fix(datetime): floor sub-second negative timestamps instead of
snapping to epoch (#262)
- chore(deps): bump rustls-webpki to 0.103.13 for RUSTSEC-2026-0104
(#263)
- fix(repos): normalize whitespace in repo identifiers before lookup
(#261)
- fix(git): parse GitHub remotes with embedded http(s) credentials
(#260)
- fix(auth): redirect browser and surface OAuth errors on PKCE callback
failure (#258)
- refactor(config): rewrite `update_config` through the locked handle
(#257)
On merge, the release workflow will tag `v0.2.2` and publish platform
artifacts via cargo-dist.
## Test plan
- [x] `cargo build` succeeds with version 0.2.2
- [ ] Tag `v0.2.2` is created on merge and release workflow publishes
artifacts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/usedetail/cli/pull/265"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] CLI: GitHub repo inference fails for HTTPS remotes with embedded credentials

1 participant

@sachiniyer