fix: disable autocomplete on admin auth + storage pages

scarlett
NGPixel 4 days ago
parent fb184f6375
commit 800fb31bbd
No known key found for this signature in database

@ -1,28 +0,0 @@
import path from 'node:path'
import fse from 'fs-extra'
export async function task(): Promise<void> {
WIKI.logger.info('Purging orphaned upload files...')
try {
const uplTempPath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads')
await fse.ensureDir(uplTempPath)
const ls = await fse.readdir(uplTempPath)
const fifteenAgo = Temporal.Now.instant().subtract({ minutes: 15 })
for (const f of ls) {
const stat = await fse.stat(path.join(uplTempPath, f))
// -> Compared as epoch millis. Temporal deliberately has no `valueOf`, so relational
// operators on its types throw — comparisons must be explicit.
if (stat.isFile() && stat.ctime.getTime() < fifteenAgo.epochMilliseconds) {
await fse.unlink(path.join(uplTempPath, f))
}
}
WIKI.logger.info('Purging orphaned upload files: [ COMPLETED ]')
} catch (err: any) {
WIKI.logger.error('Purging orphaned upload files: [ FAILED ]')
WIKI.logger.error(err.message)
throw err
}
}

@ -353,15 +353,21 @@ const hasError = computed(() => Boolean(errorMessage.value))
/*
The opt-out attributes, as one object bound in a single `v-bind`.
`autocomplete="off"` is the standards half and the only one any browser reads; the four `data-`
`autocomplete` is the standards half and the only one any browser reads; the four `data-`
attributes are what the password managers that ignore it read instead -- 1Password, LastPass,
Bitwarden and Dashlane respectively, each having settled on its own spelling. They are inert
everywhere else, so they cost a field that nobody's extension looks at nothing.
`new-password` rather than `off` on a password field, and that difference is the whole point on
the fields that matter most: Chrome deliberately disregards `off` there -- too many banks had used
it to stop people pasting -- and offers the saved credential anyway. `new-password` is the one
value it does honour, because a field being filled with an EXISTING password is exactly what it
says this is not. Every other type reads `off`, which they all honour.
*/
const autofillAttrs = computed(() =>
props.noAutofill
? {
autocomplete: 'off',
autocomplete: props.type === 'password' ? 'new-password' : 'off',
'data-1p-ignore': 'true',
'data-lpignore': 'true',
'data-bwignore': 'true',

@ -3,10 +3,16 @@
v-if="max > 1"
class="w-pagination flex flex-nowrap items-center gap-1"
:aria-label="ariaLabel">
<!--
`dark:text-white` on every button: `.w-unstyled` leaves the colour to `inherit`, and this
control is used OUTSIDE a card straight on the admin page background where nothing up the
tree sets a dark-mode colour, so the numbers were drawn in the light theme's black on a dark
page. The ellipsis beside them already carried its own pair.
-->
<button
v-if="directionLinks"
type="button"
class="w-unstyled w-pagination-btn"
class="w-unstyled w-pagination-btn dark:text-white"
:disabled="modelValue <= 1"
:aria-label="prevLabel"
@click="go(modelValue - 1)">
@ -20,7 +26,7 @@
<button
v-else
type="button"
class="w-unstyled w-pagination-btn"
class="w-unstyled w-pagination-btn dark:text-white"
:class="page === modelValue ? 'w-pagination-btn--active' : ''"
:aria-current="page === modelValue ? 'page' : undefined"
:aria-label="`${pageLabel} ${page}`"
@ -32,7 +38,7 @@
<button
v-if="directionLinks"
type="button"
class="w-unstyled w-pagination-btn"
class="w-unstyled w-pagination-btn dark:text-white"
:disabled="modelValue >= max"
:aria-label="nextLabel"
@click="go(modelValue + 1)">

@ -309,11 +309,17 @@
options-dense
:aria-label="cfg.title"
:disable="cfg.readOnly" />
<!-- -> `no-autofill` on every prop a strategy declares, not only the sensitive
ones: a manager offers to fill whatever LOOKS like a credential, and a
client ID or an issuer URL beside a secret is exactly that shape. What is
typed here is the wiki's credential with an identity provider, never the
operator's own. -->
<w-input
v-else
outlined
v-model="cfg.value"
dense
no-autofill
:type="inputTypeFor(cfg)"
:aria-label="cfg.title"
:disable="cfg.readOnly"

@ -201,11 +201,17 @@
options-dense
:aria-label="cfg.title"
:disable="cfg.readOnly" />
<!-- -> `no-autofill` on every field a module declares, not only the
sensitive ones: a manager offers to fill whatever LOOKS like a credential,
and a target's host and account name are exactly that shape. What is being
typed here is the wiki's credential for somebody else's bucket, never the
operator's own. -->
<w-input
v-else
outlined
v-model="cfg.value"
dense
no-autofill
:type="inputTypeFor(cfg)"
:aria-label="cfg.title"
:disable="cfg.readOnly"
@ -270,6 +276,7 @@
<w-input
outlined
dense
no-autofill
v-model="state.target.assetDelivery.baseUrl"
placeholder="https://files.example.com"
:aria-label="t(`admin.storage.deliveryBaseUrl`)" />
@ -288,6 +295,7 @@
<w-input
outlined
dense
no-autofill
v-model="state.target.assetDelivery.linkExpiration"
:aria-label="t(`admin.storage.deliveryExpiration`)" />
</w-item-section>
@ -544,6 +552,7 @@
<w-input
outlined
dense
no-autofill
v-model="state.largeThreshold"
:aria-label="t(`admin.storage.largeThreshold`)" />
</w-item-section>
@ -559,6 +568,7 @@
<w-input
outlined
dense
no-autofill
v-model="state.syncInterval"
:aria-label="t(`admin.storage.syncInterval`)" />
</w-item-section>

Loading…
Cancel
Save