Skip to content

Improved Usernames / Organization / Groups Path Matching in credentials - #4

Closed
macmonac wants to merge 2 commits into
ttys3:mainfrom
macmonac:main
Closed

Improved Usernames / Organization / Groups Path Matching in credentials#4
macmonac wants to merge 2 commits into
ttys3:mainfrom
macmonac:main

Conversation

@macmonac

@macmonac macmonac commented Jan 22, 2026

Copy link
Copy Markdown
Contributor

User description

The old version only supported host verification. When combined with useHttpPath, it could only check a single-level path for usernames, organizations, or groups, not the full path.
If the first line matched the host or the first-level path, those credentials were returned, even if a later line specified a more precise path.

Example:
https://USERNAME:TOKEN1@gitlab.com/group/subgroup1/project.git
https://USERNAME:TOKEN2@gitlab.com/group/subgroup2/project2.git
https://USERNAME:TOKEN3@gitlab.com/group/subgroup2/
https://USERNAME:TOKEN4@gitlab.com/group/

For the repository:
https://USERNAME:TOKEN2@gitlab.com/group/subgroup2/project2.git

Without useHttpPath: Only the host, protocol, and HTTP username (if specified) are checked. It returns TOKEN1.
With useHttpPath (old version), only the first-level path was checked, even though the rest of the path did not match. In this case, it’s the same as above, so it returns TOKEN1.

Now, The system checks if the path configured in the credentials is the beginning of the requested path.

For the same repository:
https://USERNAME:TOKEN2@gitlab.com/group/subgroup2/project2.git

Without useHttpPath: Behavior remains unchanged (returns TOKEN1).
With useHttpPath (new version): It can now match the full path or a partial path, regardless of the number of username/organization/group levels. Since the full path of the first line doesn’t match the request, it continues to the second line, which does match. It returns TOKEN2.

Notes:
The third line provides global credentials for other projects under /group/subgroup2/.
The fourth line provides global credentials for other projects under /group/.

Important:
The order of lines in the credentials file matters. More specific paths should be listed before broader ones.

I hope this doesn’t alter the originally intended behavior.

If the pull request is accepted, I think we’ll need to update the end of the documentation.


PR Type

Enhancement, Bug fix


Description

  • Improved path matching to support full paths instead of single-level

  • Now checks if configured path is prefix of requested path

  • Added support for ignoring comments in credentials file

  • Simplified path matching logic with strings.HasPrefix


Diagram Walkthrough

flowchart LR
  A["Old Path Matching"] -->|"Single-level only"| B["Limited accuracy"]
  C["New Path Matching"] -->|"Full path prefix check"| D["Better accuracy"]
  E["Comment Support"] -->|"Skip # and //"| F["Cleaner config files"]
Loading

File Walkthrough

Relevant files
Enhancement
main.go
Simplify path matching and add comment filtering                 

main.go

  • Replaced single-level path matching with strings.HasPrefix for full
    path comparison
  • Simplified path matching logic by removing strings.Cut and separate
    handling branches
  • Added comment filtering to skip lines starting with # or // in
    credentials file
  • Improved logging to show actual paths being compared
+10/-13 

macmonac and others added 2 commits January 22, 2026 01:09
The old version only supported host verification. When combined with useHttpPath, it could only check a single-level path for usernames, organizations, or groups, not the full path.
If the first line matched the host or the first-level path, those credentials were returned, even if a later line specified a more precise path.

Example:
https://USERNAME:TOKEN1@gitlab.com/group/subgroup1/project.git
https://USERNAME:TOKEN2@gitlab.com/group/subgroup2/project2.git
https://USERNAME:TOKEN3@gitlab.com/group/subgroup2/
https://USERNAME:TOKEN4@gitlab.com/group/

For the repository:
https://USERNAME:TOKEN2@gitlab.com/group/subgroup2/project2.git

    Without useHttpPath: Only the host, protocol, and HTTP username (if specified) are checked. It returns TOKEN1.
    With useHttpPath (old version), only the first-level path was checked, even though the rest of the path did not match. In this case, it’s the same as above, so it returns TOKEN1.

Now, The system checks if the path configured in the credentials is the beginning of the requested path.

For the same repository:
https://USERNAME:TOKEN2@gitlab.com/group/subgroup2/project2.git

    Without useHttpPath: Behavior remains unchanged (returns TOKEN1).
    With useHttpPath (new version): It can now match the full path or a partial path, regardless of the number of username/organization/group levels. Since the full path of the first line doesn’t match the request, it continues to the second line, which does match. It returns TOKEN2.

Notes:
    The third line provides global credentials for other projects under /group/subgroup2/.
    The fourth line provides global credentials for other projects under /group/.

Important:
    The order of lines in the credentials file matters. More specific paths should be listed before broader ones.
@ttys3

ttys3 commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Thank you for the detailed report and the concrete GitLab examples. The underlying diagnosis is correct: the previous matcher considered only one path component, so nested group/subgroup paths could select an earlier, broader credential instead of the intended one.

After reviewing the patch against the documented Git credential behavior, I did not merge it as-is for two reasons:

  1. strings.HasPrefix(req.path, c.path) is not path-segment-aware. A credential scoped to group/sub would also match group/submarine/repo.git. Because the selected value is a credential, this prefix collision is a security boundary, not only a matching bug.
  2. Treating # and // lines as comments is incompatible with the standard git-credential-store file format, which defines one credential URL per line and does not allow comment lines.

I implemented the nested-path correction separately in #6. The replacement implementation:

  • matches an exact path or a descendant separated by /, so group/sub does not match group/submarine;
  • handles trailing slashes, query/fragment boundaries, literal + characters, and single-pass percent decoding;
  • prevents passwords and malformed credential lines from being written to debug logs, and creates POSIX debug logs with 0600 permissions;
  • includes unit tests for nested scopes, prefix collisions, URL parsing/decoding, credential-protocol termination, and secure logging.

The replacement has been merged and released in v1.1.3.

Relevant Git documentation:

Thanks again. This PR identified a real limitation, and the nested GitLab examples were valuable when designing the corrected behavior and its test coverage.

@ttys3 ttys3 closed this Sep 4, 2026
Repository owner deleted a comment from qodo-free-for-open-source-projects Bot Sep 4, 2026
Repository owner deleted a comment from qodo-free-for-open-source-projects Bot Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants