Skip to content

Commit fc360d7

Browse files
Release 20260321
1 parent b260e17 commit fc360d7

9 files changed

Lines changed: 98 additions & 12 deletions

File tree

hlp-aguide/Help-cs.zip

2.2 KB
Binary file not shown.

hlp-aguide/Help-en.zip

1.96 KB
Binary file not shown.

hlp/Help-.zip

0 Bytes
Binary file not shown.

hlp/Help-cs.zip

112 Bytes
Binary file not shown.

hlp/Help-en.zip

87 Bytes
Binary file not shown.

hvdata/appmain.js

Lines changed: 91 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,29 @@ const $A = (selector, parent = document) => parent?.querySelectorAll(selector);
99

1010
function toFilteredUTFText(s) {
1111
if (!s) return s
12-
return safeFilteredText(s.normalize('NFC').replace(/[\u200B-\u200C\u200E-\u200F\uFEFF\u2060-\u206F\uE000-\uF8FF\u007F-\u009F\u2028\u2029]/g, ''));
12+
return safeFilteredText(s.normalize('NFKC').replace(/[\u200B-\u200C\u200E-\u200F\uFEFF\u2060-\u206F\uE000-\uF8FF\u007F-\u009F\u2028\u2029\uFE00\uFE0D\u202A-\u202E\u2066-\u2069\u00AD\u034F\u180E\u202F\u0080-\u009F\u115F\u1160\u17B4\uFDD0-\uFDEF\u0300-\u036F]/g, ''));
1313
// \u200D - kept for unicode character joining
1414
// \u0000-\u001F (C0), \u007F-\u009F (C1)
1515
// \u2028 - line sep, \u2029 - par sep.
1616
// \u0000-\u001F ... kept unsolved because of browser loading stooped due to this!
17+
// \uFE00–\uFE0F - think about this - fall unicode icons to no color forms, but improves security
1718
}
1819

1920
function safeFilteredText(s) {
2021
if (!s) return s;
21-
return s.split('').filter((c, i) => {
22-
const code = c.charCodeAt(0);
23-
return code >= 32 || [9,10,13].includes(code);
24-
})
25-
.join('');
22+
let out = '';
23+
for (const ch of s) {
24+
const cp = ch.codePointAt(0);
25+
26+
// skip: SMP variation selectors
27+
if (cp >= 0xE0100 && cp <= 0xE01EF) continue;
28+
29+
// keep: TAB (9), LF (10), CR (13), 32 and higher
30+
if (cp >= 32 || cp === 9 || cp === 10 || cp === 13) {
31+
out += ch;
32+
}
33+
}
34+
return out;
2635
}
2736

2837
function newUID(length = 8) {
@@ -240,7 +249,9 @@ var _Storage = (() => {
240249
if (!storagesC.has(key))
241250
return null;
242251

243-
return await storagesC.get(key).searchImage(filePath);
252+
let rawData = await storagesC.get(key).searchImage(filePath);
253+
rawData = await doSteganographyCorrectionForImage(rawData || filePath);
254+
return rawData;
244255
}
245256

246257
function getKey(key) {
@@ -259,6 +270,56 @@ var _Storage = (() => {
259270
};
260271
})();
261272

273+
async function doSteganographyCorrectionForImage(data) {
274+
const img = new Image();
275+
if (data.startsWith('data:image/'))
276+
img.src = data;
277+
else {
278+
const response = await fetchDataOrZero(data);
279+
if (response.byteLength == 0)
280+
return null;
281+
282+
const content = btoa(String.fromCharCode(...new Uint8Array(response)));
283+
img.src = `data:image/${data.split('.').pop().toLowerCase()};base64,${content}`;
284+
}
285+
286+
await new Promise((resolve, reject) => {
287+
img.onload = resolve;
288+
img.onerror = reject;
289+
});
290+
291+
const canvas = document.createElement('canvas');
292+
const ctx = canvas.getContext('2d');
293+
canvas.width = img.width;
294+
canvas.height = img.height;
295+
ctx.clearRect(0, 0, canvas.width, canvas.height);
296+
297+
if (canvas.width > 16 || canvas.height > 16) {
298+
// exception : prevent blur of edges and lines in image
299+
const tempCanvas = document.createElement('canvas');
300+
const tempCtx = tempCanvas.getContext('2d');
301+
tempCanvas.width = Math.ceil(img.width * 1.005);
302+
tempCanvas.height = Math.ceil(img.height * 1.005);
303+
tempCtx.drawImage(img, 0, 0, tempCanvas.width, tempCanvas.height);
304+
ctx.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height);
305+
} else {
306+
ctx.drawImage(img, 0, 0);
307+
}
308+
309+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
310+
const pixels = imageData.data;
311+
for (let i = 0; i < pixels.length; i += 4) {
312+
pixels[i] = (pixels[i] & ~1) | (Math.random() < 0.5 ? 1 : 0);
313+
pixels[i+1] = (pixels[i+1] & ~1) | (Math.random() < 0.5 ? 1 : 0);
314+
pixels[i+2] = (pixels[i+2] & ~1) | (Math.random() < 0.5 ? 1 : 0);
315+
pixels[i+3] = (pixels[i+3] & ~1) | (Math.random() < 0.5 ? 1 : 0);
316+
}
317+
ctx.putImageData(imageData, 0, 0);
318+
319+
const canvasReply = canvas.toDataURL('image/png');
320+
return canvasReply;
321+
}
322+
262323
/**
263324
* @interface
264325
*/
@@ -270,24 +331,43 @@ class IStorage {
270331
}
271332

272333
class StorageZip extends IStorage {
334+
#storageO;
335+
273336
constructor() {
274337
super();
275-
this.storageO = null;
338+
this.#storageO = null;
276339
}
277340

278341
async init(path) {
279-
this.storageO = await ZIPHelpers.loadZipFromUrl(path);
342+
this.#storageO = await ZIPHelpers.loadZipFromUrl(path);
343+
let hasSlip = false;
344+
const paths = Object.keys(this.#storageO.files);
345+
for (const path of paths) {
346+
if (path.includes('..')) {
347+
delete this.#storageO.files[path];
348+
hasSlip = true;
349+
log('E Zip Slip:', path);
350+
}
351+
}
352+
if (hasSlip) {
353+
const msg = `Invalid paths found in given archive <b>${path}</b>. Loading stopped for ensuring security.`;
354+
log(`E ${msg}`);
355+
let pane = $O('#cors-error') || $O('#content') || document.body;
356+
pane.innerHTML = `⚠ ${msg}`;
357+
throw new Error(msg);
358+
}
359+
Object.freeze(this.#storageO.files);
280360
return this;
281361
}
282362

283363
async search(filePath, format = STOF_TEXT) {
284-
return await ZIPHelpers.searchArchiveForFile(filePath, this.storageO, format);
364+
return await ZIPHelpers.searchArchiveForFile(filePath, this.#storageO, format);
285365
}
286366

287367
async getSubdirs(parentPath) {
288368
const subdirs = new Set();
289369

290-
this.storageO?.forEach((relativePath, file) => {
370+
this.#storageO?.forEach((relativePath, file) => {
291371
if (relativePath.startsWith(parentPath) && relativePath !== parentPath)
292372
{
293373
const subPath = relativePath.slice(parentPath.length);

hvdata/data.zip

138 Bytes
Binary file not shown.

index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
<title>Help Viewer</title>
77
<meta name="application-name" content="Help Viewer">
88
<meta name="description" content="Help Viewer help file.">
9+
<meta http-equiv="Content-Security-Policy" content="
10+
default-src * 'unsafe-inline' 'unsafe-eval';
11+
connect-src * http: https: file:;
12+
img-src * data: blob: https: http: 'unsafe-inline';
13+
font-src data:;
14+
">
915
<link rel="manifest" href="manifest.webmanifest">
1016
<script src="hvdata/jszip.min.js"></script>
1117
<script>

sw.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const CACHE_NAME = 'helpviewer-cache-20260310';
1+
const CACHE_NAME = 'helpviewer-cache-20260321';
22
const CACHE_FILES = [ '/favicon.png','/index.html','/manifest.webmanifest','/LICENSE','/hvdata/data.zip','/hvdata/index.html','/hvdata/jszip.min.js','/hvdata/LICENSE-jszip.md','/hvdata/appmain.js','/faviconPWA.png','/robots.txt' ];
33

44
self.addEventListener('install', (event) => {

0 commit comments

Comments
 (0)