mirror of
https://github.com/wekan/wekan.git
synced 2026-11-08 11:25:19 -05:00
One unified filename subsystem, used everywhere a name is shown, uploaded,
served, or migrated. Design: docs/Features/Filename/Filename.md.
Display (imports/lib/fileNameDisplay.js, general cleanFileName + global
{{cleanFilename}}/{{downloadFilename}} helpers): URL-decode, normalize to
generally-used characters (NFKC + confusable-homoglyph folding so typosquatting
"payp<Cyrillic-a>l.exe" shows as "paypal.exe", only in a Latin-majority base so
genuine non-Latin names are preserved), remove invisible/control/bidi chars,
strip HTML/JS/XML/template markup. Removed the earlier invisible-char filter
button, warning triangle, inline descriptions, legend and server invisibleOnly
filter.
Upload (attachments + avatars, all backends): reject exploit-looking filenames
and EICAR virus test files; strip invisible chars; correct the extension to the
real detected type (double-click opens the right app); type-based name when
empty; cap length to a portable max (30, Amiga OS FFS) preserving the extension;
sanitize SVG JS / XML-loop content at the staging location before promoting to
final storage and delete the temp file after; enough-disk-space check with
small-RAM streaming and stop-and-remove-partial on any write error.
Existing files: general storage-agnostic detector streams a small header to
WRITABLE_PATH/files/temp, runs `file`, deletes the temp file; admin-only bounded
batch extension corrector; serve-time on-the-fly exploit sanitizer wired into
httpStreamOutput (sniffs the start, rewrites only dangerous markup, streams the
rest unchanged).
Migrations (moveToStorage/copyFile): sanitize content, finalize the name with
the same general function (detected type, corrected extension, folded
homoglyphs, capped length), save it at the destination, and number
same-name/different-content collisions (document.pdf, document-1.pdf, ...).
Tests: rewrote fileNameDisplay.test.cjs; added uploadFileName, diskSpace,
fileTypeCorrection and fileHardeningGuards unit tests (positive + negative);
updated Playwright spec 41 to the clean-name model. CHANGELOG + design doc.
102 lines
2.9 KiB
CSS
102 lines
2.9 KiB
CSS
/* Search + pagination controls for the admin report tabs (files, rules,
|
|
boards, cards). Styled to match the admin People panel (peopleBody.css). */
|
|
|
|
/* One controls row per report: the search field on the left, the pagination
|
|
controls pushed to the right (margin-inline-start:auto mirrors automatically in
|
|
RTL). No separate Search button — typing + Enter searches. The controls use the
|
|
theme accent (Member Settings → Change color sets --theme-accent on :root),
|
|
falling back to WeKan blue when no custom theme is chosen. */
|
|
.admin-report-controls {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.admin-report-controls input {
|
|
flex: 0 1 320px;
|
|
max-width: 320px;
|
|
padding: 0 8px;
|
|
height: 34px;
|
|
}
|
|
|
|
.admin-report-pagination {
|
|
margin-inline-start: auto; /* push pagination to the end of the controls row */
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.admin-report-pagination button {
|
|
min-width: 32px;
|
|
height: 30px;
|
|
border: 1px solid var(--theme-accent, #01628c);
|
|
background: transparent;
|
|
color: var(--theme-accent, #01628c);
|
|
border-radius: 0.4vw;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.admin-report-pagination button:hover:not(.disabled) {
|
|
background: var(--theme-accent, #01628c);
|
|
color: #fff;
|
|
}
|
|
|
|
.admin-report-pagination button.active {
|
|
background: var(--theme-accent, #01628c);
|
|
border-color: var(--theme-accent, #01628c);
|
|
color: #fff;
|
|
}
|
|
|
|
.admin-report-pagination button.disabled {
|
|
opacity: 0.5;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.admin-report-page-info {
|
|
min-width: 72px;
|
|
text-align: center;
|
|
}
|
|
|
|
/* Problems page: menu separator between the event streams (Summary/Security/
|
|
Speed/Tests) and the moved report items, and the read-only event table. */
|
|
.admin-reports-content .side-menu .admin-reports-separator hr {
|
|
margin: 6px 0;
|
|
border: 0;
|
|
border-top: 1px solid rgba(0,0,0,0.15);
|
|
}
|
|
.admin-event-controls {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 8px;
|
|
}
|
|
.admin-event-controls input {
|
|
flex: 0 1 320px;
|
|
max-width: 320px;
|
|
height: 34px;
|
|
padding: 0 8px;
|
|
}
|
|
.admin-event-controls .admin-event-pagination {
|
|
margin-inline-start: auto;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.admin-event-controls .admin-event-pagination a {
|
|
cursor: pointer;
|
|
color: var(--theme-accent, #01628c);
|
|
font-weight: bold;
|
|
padding: 0 6px;
|
|
}
|
|
.admin-event-controls .admin-event-pagination a.disabled { opacity: 0.4; pointer-events: none; }
|
|
table.admin-event-table { width: 100%; border-collapse: collapse; }
|
|
table.admin-event-table th, table.admin-event-table td {
|
|
text-align: start; padding: 4px 8px; border-bottom: 1px solid rgba(0,0,0,0.08); vertical-align: top;
|
|
}
|
|
table.admin-event-table .admin-event-at { white-space: nowrap; }
|
|
table.admin-event-table .admin-event-sev[data-sev="high"],
|
|
table.admin-event-table .admin-event-sev[data-sev="critical"] { color: #c0392b; font-weight: bold; }
|
|
table.admin-event-table .admin-event-detail { word-break: break-word; }
|