mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Disable form autofill (#17291)
]* fix aria-hidden and tabindex
* use {{template "base/disable_form_autofill"}} instead of {{DisableFormAutofill}}
Co-authored-by: zeripath <art27@cantab.net>
			
			
This commit is contained in:
		| @@ -8,6 +8,7 @@ | ||||
| 		</h4> | ||||
| 		<div class="ui attached segment"> | ||||
| 			<form class="ui form" action="{{.Link}}" method="post"> | ||||
| 				{{template "base/disable_form_autofill"}} | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 				<input type="hidden" name="id" value="{{.Source.ID}}"> | ||||
| 				<div class="inline field"> | ||||
| @@ -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}}"> | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| 		</h4> | ||||
| 		<div class="ui attached segment"> | ||||
| 			<form class="ui form" action="{{.Link}}" method="post"> | ||||
| 				{{template "base/disable_form_autofill"}} | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 				<!-- Types and name --> | ||||
| 				<div class="inline required field {{if .Err_Type}}error{{end}}"> | ||||
|   | ||||
| @@ -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}}"> | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| 		</h4> | ||||
| 		<div class="ui attached segment"> | ||||
| 			<form class="ui form" action="{{.Link}}" method="post"> | ||||
| 				{{template "base/disable_form_autofill"}} | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 				<div class="field {{if .Err_UserName}}error{{end}}"> | ||||
| 					<label for="user_name">{{.i18n.Tr "username"}}</label> | ||||
| @@ -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"> | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| 		</h4> | ||||
| 		<div class="ui attached segment"> | ||||
| 			<form class="ui form" action="{{.Link}}" method="post"> | ||||
| 				{{template "base/disable_form_autofill"}} | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 				<!-- Types and name --> | ||||
| 				<div class="inline required field {{if .Err_LoginType}}error{{end}}"> | ||||
| @@ -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}}> | ||||
|   | ||||
							
								
								
									
										31
									
								
								templates/base/disable_form_autofill.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								templates/base/disable_form_autofill.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| {{/* | ||||
| 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. | ||||
| */}} | ||||
| <div class="autofill-dummy" aria-hidden="true"> | ||||
| 	<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> | ||||
| @@ -3,6 +3,7 @@ | ||||
| 	<div class="ui middle very relaxed page grid"> | ||||
| 		<div class="column"> | ||||
| 			<form class="ui form" action="{{.Link}}" method="post"> | ||||
| 				{{template "base/disable_form_autofill"}} | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 				<h3 class="ui top attached header"> | ||||
| 					{{.i18n.Tr "repo.migrate.migrate" .service.Title}} | ||||
| @@ -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}}"> | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| 	<div class="ui middle very relaxed page grid"> | ||||
| 		<div class="column"> | ||||
| 			<form class="ui form" action="{{.Link}}" method="post"> | ||||
| 				{{template "base/disable_form_autofill"}} | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 				<h3 class="ui top attached header"> | ||||
| 					{{.i18n.Tr "repo.migrate.migrate" .service.Title}} | ||||
| @@ -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}}"> | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| 		</h4> | ||||
| 		<div class="ui attached segment"> | ||||
| 			<form class="ui form" action="{{.Link}}" method="post"> | ||||
| 				{{template "base/disable_form_autofill"}} | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 				<input type="hidden" name="action" value="update"> | ||||
| 				<div class="required field {{if .Err_RepoName}}error{{end}}"> | ||||
| @@ -104,6 +105,7 @@ | ||||
| 						<tr> | ||||
| 							<td colspan="4"> | ||||
| 								<form class="ui form" method="post"> | ||||
| 									{{template "base/disable_form_autofill"}} | ||||
| 									{{.CsrfTokenHtml}} | ||||
| 									<input type="hidden" name="action" value="mirror"> | ||||
| 									<div class="inline field {{if .Err_EnablePrune}}error{{end}}"> | ||||
| @@ -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"> | ||||
| @@ -195,11 +196,12 @@ | ||||
| 							<tr> | ||||
| 								<td colspan="4"> | ||||
| 									<form class="ui form" method="post"> | ||||
| 										{{template "base/disable_form_autofill"}} | ||||
| 										{{.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}}> | ||||
| @@ -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"> | ||||
|   | ||||
| @@ -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"> | ||||
| 		{{template "base/disable_form_autofill"}} | ||||
| 		{{.CsrfTokenHtml}} | ||||
| 		<div class="required field {{if .Err_PayloadURL}}error{{end}}"> | ||||
| 			<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label> | ||||
| @@ -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"> | ||||
|   | ||||
| @@ -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"> | ||||
| 		{{template "base/disable_form_autofill"}} | ||||
| 		{{.CsrfTokenHtml}} | ||||
| 		<div class="required field {{if .Err_PayloadURL}}error{{end}}"> | ||||
| 			<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label> | ||||
| @@ -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"> | ||||
|   | ||||
| @@ -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"> | ||||
| 				{{template "base/disable_form_autofill"}} | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 				{{if .SignedUser.IsPasswordSet}} | ||||
| 				<div class="required field {{if .Err_OldPassword}}error{{end}}"> | ||||
| @@ -178,8 +179,8 @@ | ||||
| 				{{ end }} | ||||
| 			</div> | ||||
| 			<form class="ui form ignore-dirty" id="delete-form" action="{{AppSubUrl}}/user/settings/account/delete" method="post"> | ||||
| 				{{template "base/disable_form_autofill"}} | ||||
| 				{{.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> | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user