Skip to content

sysutils/git-backup: don't accept credentials in the backup url - #5702

Merged
AdSchellevis merged 1 commit into
opnsense:masterfrom
Lux-WorldPC:git-backup-preserve-url-user
Sep 6, 2026
Merged

sysutils/git-backup: don't accept credentials in the backup url#5702
AdSchellevis merged 1 commit into
opnsense:masterfrom
Lux-WorldPC:git-backup-preserve-url-user

Conversation

@Lux-WorldPC

@Lux-WorldPC Lux-WorldPC commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Important notices

Before you submit a pull request, we ask you kindly to acknowledge the following:

If AI was used, please disclose:

  • Model used: Claude Opus 5, via Claude Code
  • Extent of AI involvement: analysis, the patch and the test scripts. Every result below comes from
    a run through the web form on a real installation, and I reviewed each one before posting.

Describe the problem

The url mask allows a userinfo section, so a url that already carries a user passes validation.
Git::backup() then adds the User Name field on top — mandatory once the backup is enabled — and
the remote ends up with the user twice:

ssh://git@codeberg.org/user/project.git  +  User Name "git"
   ->  ssh://git@git@codeberg.org/user/project.git

The push never authenticates and nothing is backed up. Reported in #5477 and #5606.


Describe the proposed solution

As you suggested, repair the validation rather than the code: exclude the userinfo part of the
authority from the mask, and say so in the validation message. Credentials have their own User Name
and Password fields, and the field help only ever showed user-less examples. Git.php is untouched.
The exclusion covers the authority only, so an @ stays legal inside the path.

Tested on 26.1.11_10 with os-git-backup 1.1_3, through the form on diag_backup.php, against an
ssh remote and an nginx/git-http-backend remote. Of nineteen urls, four change — the two from the
reports plus https://user@host/repo.git and https://user:token@host/repo.git. Ports, an IPv6
literal and an @ inside the path stay accepted; git@github.com:user/project.git stays rejected.
Both intended forms still save and push, over ssh and over https. Credentials in the authority are
the only thing that changes: an empty authority and an empty scheme are still accepted, exactly as
before.

Neither credential-bearing form works today, so nothing that currently backs up becomes invalid:
with such a url stored directly in config.xml, the remote comes out as ssh://git@git@host/...
and https://git:pw@git:pw@host/..., and no commit reaches the server.

One limitation worth stating: this does not reach installations that already stored such a url.
performValidation() only validates fields that changed (BaseModel.php:637), so re-saving that
page with the url untouched passes the new mask — I ran it, and changing only the branch too; both
report authentication failure with nothing pointing at the url. The new message appears only once
that field is edited. No migration, as you said: the model's treatment of the data is unchanged.

PLUGIN_REVISION left alone.


Related issue

Closes #5477
Refs #5606, #5629

@AdSchellevis

Copy link
Copy Markdown
Member

It's probably better to improve the field validation as we're looking at an input error here (I don't think usernames inside uri's where ever supported here, in which case we can just change the regex to exclude the user section)

@Lux-WorldPC

Copy link
Copy Markdown
Contributor Author

Thanks — that's fair. The field help and the validation message both show user-less examples, so a username in the URL was never intended input here.

One consequence worth weighing before I rework it: validation only runs on save. Git::backup() reads the stored model directly and never calls validateModel(), so a stricter mask stops new bad input but does not repair the installations already configured that way — which is the case in all three reports. They would be told the next time they open that page, since the stored value would fail the new mask on save, but until someone does the backup keeps failing silently.

Measured on 26.1.11_10 against a real SSH remote, same stored config.xml in every row:

stored url mask Git.php result
ssh://git@host/repo.git, typed in tightened package rejected on save, as intended
ssh://git@host/repo.git, already stored tightened package authentication failure, remote ssh://git@git@host/...
ssh://git@host/repo.git, already stored current package authentication failure, remote ssh://git@git@host/...
ssh://git@host/repo.git, already stored current this PR push ok, commit visible on the remote

The last two rows differ only in Git.php; the middle two only in the mask.

So it comes down to whether those setups should recover on their own or be asked to fix their input. Your call, and I am happy either way.

If validation only, this one came back clean on the ten inputs I ran through setConfiguration() on that box — it accepts ssh://server/project.git, https://server/project.git, explicit ports and an @ inside the path, and rejects a user in the authority:

<Mask>/^((https)|(ssh))?:\/\/[^\/@]+(\/.*)?[^\/]$/</Mask>

paired with a validation message pointing at the User Name field. The strpos() guard in my patch is already unreachable through the form — I checked, the current mask rejects ssh:/host/project.git and host/project.git alike — so it can go as well; it only went in as a belt-and-braces for #5629.

Let me know which you would prefer and I will rewrite the PR.

@AdSchellevis

Copy link
Copy Markdown
Member

Usually if saved data should be changed due to model changes, we use migrations to update the data, but only when the data should be altered due to a change in how the model treats the data underneath (e.g. we split one input into two).

In this case there's just an omission in the validation allowing more than it should, which would plea for just repairing the validation. These things are quite common, we tighten validations frequently usually because the input or combinations of inputs aren't actually supported by the technology underneath.

The url mask allows a userinfo section, and Git::backup() adds the mandatory
User Name field on top, so an ssh url that already carries a user ends up as
ssh://git@git@host/... and the push never authenticates. Credentials belong in
the User Name and Password fields, so exclude them from the url.

Closes opnsense#5477

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lux-WorldPC
Lux-WorldPC force-pushed the git-backup-preserve-url-user branch from e0fef91 to a3cb83e Compare September 6, 2026 09:09
@Lux-WorldPC Lux-WorldPC changed the title sysutils/git-backup: don't inject a user name into a url that already has one sysutils/git-backup: don't accept credentials in the backup url Sep 6, 2026
@Lux-WorldPC

Copy link
Copy Markdown
Contributor Author

Rewritten as a validation change only: GitSettings.xml, mask and validation message, with Git.php back at the packaged version. The updated description carries the measurements, all taken through the form on diag_backup.php against real ssh and https remotes.

One correction I owe you from my earlier comment. I wrote that already-configured installations would be told the next time they saved that page. That is not right, and I should have measured it before saying it. performValidation() only validates fields that changed (BaseModel.php:637), so re-saving with the url untouched passes the new mask — I ran it, along with changing only the branch, and both simply report authentication failure with nothing pointing at the url. The new message appears only once that field is edited. It does not affect the direction you chose, but I did not want to leave it standing.

I also checked that neither credential-bearing form works today, over ssh and over https, so no setup that currently backs up successfully becomes invalid.

@AdSchellevis
AdSchellevis merged commit 261c32e into opnsense:master Sep 6, 2026
@AdSchellevis

Copy link
Copy Markdown
Member

@Lux-WorldPC looks good, thanks!

@AdSchellevis AdSchellevis self-assigned this Sep 6, 2026
@AdSchellevis AdSchellevis added the cleanup Low impact changes label Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cleanup Low impact changes

Development

Successfully merging this pull request may close these issues.

os-git-backup: codeberg (forgejo) repos fail authentication

2 participants