Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable form autofill #17291

Merged
merged 8 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from 5 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
37 changes: 37 additions & 0 deletions modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ func NewFuncMap() []template.FuncMap {
"MermaidMaxSourceCharacters": func() int {
return setting.MermaidMaxSourceCharacters
},
"DisableFormAutofill": disableFormAutofill,
}}
}

Expand Down Expand Up @@ -965,3 +966,39 @@ func mirrorRemoteAddress(m models.RemoteMirrorer) remoteAddress {

return a
}

func disableFormAutofill() template.HTML {
/*
Why we need to disable form autofill:
1. Many pages contain different password inputs for different usages, eg: repo setting, autofill will make a mess.
2. We have `areYouSure` confirm dialog if a user leaves a pages without submit.
Autofill will make the form changed even if the user didn't input anything. Then the user keeps seeing annoying confirm dialog.

In history, Gitea put `<input class="fake" type="password">` in forms to bypass the autofill,
but there were still many forms suffered the autofill problem.

Now we improve it.

Solutions which do NOT work:
1. Adding `autocomplete=off` doesn't help. New Chrome completely ignores it.
2. Use a JavaScript to run in a few seconds later after the page is loaded to process the autofilled inputs, it doesn't work.
Because for security reason, the inputs won't be filled before the user makes an interaction in the page.
So we can not predict the correct time to run the JavaScript code.

Solutions which work:
1. Some hacky methods like: https://github.com/matteobad/detect-autofill
2. This solution: use invisible inputs. Be aware of:
(a) The inputs must be at the beginning of the form, and can not be hidden.
(b) The input for username must have a valid name.
(c) There should be no negative word (eg: fake) in the `name` attribute.
(d) Chrome seems to use a weighted algorithm to choose an input to fill text, so the using "username" as input name is better than using "user".
We make the names of these dummy inputs begin with an underline to indicate it is for special usage,
and these dummy form values won't be used by backend code.
*/
return `
<div class="autofill-dummy" aria-hidden="true">
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
<input type="text" name="_autofill_dummy_username" class="ays-ignore" tabindex="-1">
<input type="password" name="_autofill_dummy_password" class="ays-ignore" tabindex="-1">
</div>
`
}
2 changes: 1 addition & 1 deletion templates/admin/auth/edit.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</h4>
<div class="ui attached segment">
<form class="ui form" action="{{.Link}}" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<input type="hidden" name="id" value="{{.Source.ID}}">
<div class="inline field">
Expand Down Expand Up @@ -55,7 +56,6 @@
<label for="bind_dn">{{.i18n.Tr "admin.auths.bind_dn"}}</label>
<input id="bind_dn" name="bind_dn" value="{{$cfg.BindDN}}" placeholder="e.g. cn=Search,dc=mydomain,dc=com">
</div>
<input class="fake" type="password">
<div class="field">
<label for="bind_password">{{.i18n.Tr "admin.auths.bind_password"}}</label>
<input id="bind_password" name="bind_password" type="password" value="{{$cfg.BindPassword}}">
Expand Down
1 change: 1 addition & 0 deletions templates/admin/auth/new.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</h4>
<div class="ui attached segment">
<form class="ui form" action="{{.Link}}" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<!-- Types and name -->
<div class="inline required field {{if .Err_Type}}error{{end}}">
Expand Down
1 change: 0 additions & 1 deletion templates/admin/auth/source/ldap.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
<label for="bind_dn">{{.i18n.Tr "admin.auths.bind_dn"}}</label>
<input id="bind_dn" name="bind_dn" value="{{.bind_dn}}" placeholder="e.g. cn=Search,dc=mydomain,dc=com">
</div>
<input class="fake" type="password">
<div class="ldap field {{if not (eq .type 2)}}hide{{end}}">
<label for="bind_password">{{.i18n.Tr "admin.auths.bind_password"}}</label>
<input id="bind_password" name="bind_password" type="password" autocomplete="off" value="{{.bind_password}}">
Expand Down
2 changes: 1 addition & 1 deletion templates/admin/user/edit.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</h4>
<div class="ui attached segment">
<form class="ui form" action="{{.Link}}" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<div class="field {{if .Err_UserName}}error{{end}}">
<label for="user_name">{{.i18n.Tr "username"}}</label>
Expand Down Expand Up @@ -67,7 +68,6 @@
<label for="email">{{.i18n.Tr "email"}}</label>
<input id="email" name="email" type="email" value="{{.User.Email}}" autofocus required>
</div>
<input class="fake" type="password">
<div class="local field {{if .Err_Password}}error{{end}} {{if not (or (.User.IsLocal) (.User.IsOAuth2))}}hide{{end}}">
<label for="password">{{.i18n.Tr "password"}}</label>
<input id="password" name="password" type="password" autocomplete="new-password">
Expand Down
2 changes: 1 addition & 1 deletion templates/admin/user/new.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</h4>
<div class="ui attached segment">
<form class="ui form" action="{{.Link}}" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<!-- Types and name -->
<div class="inline required field {{if .Err_LoginType}}error{{end}}">
Expand Down Expand Up @@ -61,7 +62,6 @@
<label for="email">{{.i18n.Tr "email"}}</label>
<input id="email" name="email" type="email" value="{{.email}}" required>
</div>
<input class="fake" type="password">
<div class="required local field {{if .Err_Password}}error{{end}} {{if not (eq .login_type "0-0")}}hide{{end}}">
<label for="password">{{.i18n.Tr "password"}}</label>
<input id="password" name="password" type="password" autocomplete="new-password" value="{{.password}}" {{if eq .login_type "0-0"}}required{{end}}>
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/migrate/git.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form" action="{{.Link}}" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<h3 class="ui top attached header">
{{.i18n.Tr "repo.migrate.migrate" .service.Title}}
Expand All @@ -21,7 +22,6 @@
<label for="auth_username">{{.i18n.Tr "username"}}</label>
<input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
</div>
<input class="fake" type="password">
<div class="inline field {{if .Err_Auth}}error{{end}}">
<label for="auth_password">{{.i18n.Tr "password"}}</label>
<input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/migrate/onedev.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form" action="{{.Link}}" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<h3 class="ui top attached header">
{{.i18n.Tr "repo.migrate.migrate" .service.Title}}
Expand All @@ -22,7 +23,6 @@
<label for="auth_username">{{.i18n.Tr "username"}}</label>
<input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
</div>
<input class="fake" type="password">
<div class="inline field {{if .Err_Auth}}error{{end}}">
<label for="auth_password">{{.i18n.Tr "password"}}</label>
<input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
Expand Down
7 changes: 4 additions & 3 deletions templates/repo/settings/options.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</h4>
<div class="ui attached segment">
<form class="ui form" action="{{.Link}}" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="update">
<div class="required field {{if .Err_RepoName}}error{{end}}">
Expand Down Expand Up @@ -104,6 +105,7 @@
<tr>
<td colspan="4">
<form class="ui form" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="mirror">
<div class="inline field {{if .Err_EnablePrune}}error{{end}}">
Expand Down Expand Up @@ -132,7 +134,6 @@
<label for="mirror_username">{{.i18n.Tr "username"}}</label>
<input id="mirror_username" name="mirror_username" value="{{$address.Username}}" {{if not .mirror_username}}data-need-clear="true"{{end}}>
</div>
<input class="fake" type="password">
<div class="inline field {{if .Err_Auth}}error{{end}}">
<label for="mirror_password">{{.i18n.Tr "password"}}</label>
<input id="mirror_password" name="mirror_password" type="password" placeholder="{{if $address.Password}}{{.i18n.Tr "repo.mirror_password_placeholder"}}{{else}}{{.i18n.Tr "repo.mirror_password_blank_placeholder"}}{{end}}" value="" {{if not .mirror_password}}data-need-clear="true"{{end}} autocomplete="off">
Expand Down Expand Up @@ -195,11 +196,12 @@
<tr>
<td colspan="4">
<form class="ui form" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="push-mirror-add">
<div class="field {{if .Err_PushMirrorAddress}}error{{end}}">
<label for="push_mirror_address">{{.i18n.Tr "repo.settings.mirror_settings.push_mirror.remote_url"}}</label>
<input id="push_mirror_address" name="push_mirror_address" value="{{.push_mirror_address}}" autocomplete="off" required>
<input id="push_mirror_address" name="push_mirror_address" value="{{.push_mirror_address}}" required>
<p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p>
</div>
<details class="ui optional field" {{if or .Err_PushMirrorAuth .push_mirror_username}}open{{end}}>
Expand All @@ -211,7 +213,6 @@
<label for="push_mirror_username">{{.i18n.Tr "username"}}</label>
<input id="push_mirror_username" name="push_mirror_username" value="{{.push_mirror_username}}">
</div>
<input class="fake" type="password">
<div class="inline field {{if .Err_PushMirrorAuth}}error{{end}}">
<label for="push_mirror_password">{{.i18n.Tr "password"}}</label>
<input id="push_mirror_password" name="push_mirror_password" type="password" value="{{.push_mirror_password}}" autocomplete="off">
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/settings/webhook/gitea.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{{if eq .HookType "gitea"}}
<p>{{.i18n.Tr "repo.settings.add_webhook_desc" "https://docs.gitea.io/en-us/webhooks/" | Str2html}}</p>
<form class="ui form" action="{{.BaseLink}}/gitea/{{or .Webhook.ID "new"}}" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_PayloadURL}}error{{end}}">
<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label>
Expand Down Expand Up @@ -30,7 +31,6 @@
</div>
</div>
</div>
<input class="fake" type="password">
<div class="field {{if .Err_Secret}}error{{end}}">
<label for="secret">{{.i18n.Tr "repo.settings.secret"}}</label>
<input id="secret" name="secret" type="password" value="{{.Webhook.Secret}}" autocomplete="off">
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/settings/webhook/gogs.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{{if eq .HookType "gogs"}}
<p>{{.i18n.Tr "repo.settings.add_webhook_desc" "https://docs.gitea.io/en-us/webhooks/" | Str2html}}</p>
<form class="ui form" action="{{.BaseLink}}/gogs/{{or .Webhook.ID "new"}}" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_PayloadURL}}error{{end}}">
<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label>
Expand All @@ -18,7 +19,6 @@
</div>
</div>
</div>
<input class="fake" type="password">
<div class="field {{if .Err_Secret}}error{{end}}">
<label for="secret">{{.i18n.Tr "repo.settings.secret"}}</label>
<input id="secret" name="secret" type="password" value="{{.Webhook.Secret}}" autocomplete="off">
Expand Down
3 changes: 2 additions & 1 deletion templates/user/settings/account.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<div class="ui attached segment">
{{if or (.SignedUser.IsLocal) (.SignedUser.IsOAuth2)}}
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/user/settings/account" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
{{if .SignedUser.IsPasswordSet}}
<div class="required field {{if .Err_OldPassword}}error{{end}}">
Expand Down Expand Up @@ -178,8 +179,8 @@
{{ end }}
</div>
<form class="ui form ignore-dirty" id="delete-form" action="{{AppSubUrl}}/user/settings/account/delete" method="post">
{{DisableFormAutofill}}
{{.CsrfTokenHtml}}
<input class="fake" type="password">
<div class="required field {{if .Err_Password}}error{{end}}">
<label for="password-confirmation">{{.i18n.Tr "password"}}</label>
<input id="password-confirmation" name="password" type="password" autocomplete="off" required>
Expand Down
9 changes: 6 additions & 3 deletions web_src/less/_base.less
Original file line number Diff line number Diff line change
Expand Up @@ -962,10 +962,13 @@ a.ui.card:hover,
}

.form {
.fake {
display: none !important;
.autofill-dummy {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
z-index: -10000;
}

.sub.field {
margin-left: 25px;
}
Expand Down