feat(tags): QR-Aufkleber, Geraeteseite, Etikettendruck und Scanner
Der Einstieg, fuer den die Anwendung gebaut ist: scannen, sehen was zu tun ist,
loslegen. `/t/<tagCode>` kennt drei Faelle — registriertes Geraet, vorgedruckter
Tag, unbekannter Code —, die Geraeteseite zeigt Zuweisungen, Schnellstart und
Historie, und beides ist am Handy bedienbar.
Im QR steht ausschliesslich `{APP_URL}/t/{tagCode}`: kein Name, keine
Seriennummer, kein Besitzer. Ein Aufkleber haengt jahrelang sichtbar am Geraet
und wird von jedem Telefon gelesen; was darauf steht, ist veroeffentlicht. Die
Masse sind gemessen, nicht behauptet: ein echtes PDF liefert 62 x 29 mm und A4,
und ein bei 300 dpi gerastertes Etikett wird mit demselben Decoder
zurueckgelesen, den auch der Scanner benutzt.
Drei Defekte zeigte erst der gebaute Stand, alle an den Naehten zwischen den
parallel gebauten Teilen: „Starten“ setzte IN_ARBEIT und sprang nicht, „Vorlage
zuweisen“ bot Vorlagen an, die der Dienst danach ablehnte, und die Geraeteliste
verlinkte nichts. Der unabhaengige Review fand drei Blocker — darunter, dass der
Scanner nie die native BarcodeDetector-API benutzte und dass ein Unterpfad in
APP_URL aus dem gedruckten QR herausfiel.
Closes #14
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c78a6ac1af
commit
5f1b0d1435
@@ -29,8 +29,16 @@ function teile(richtlinie: string): Map<string, string> {
|
||||
describe("Ausgelieferter Stand", () => {
|
||||
const richtlinie = sicherheitsrichtlinie(NONCE, false);
|
||||
|
||||
it("erlaubt kein unsafe-eval", () => {
|
||||
expect(richtlinie).not.toContain("unsafe-eval");
|
||||
it("erlaubt kein unsafe-eval – wohl aber WebAssembly", () => {
|
||||
/*
|
||||
* `wasm-unsafe-eval` enthaelt die Zeichenkette „unsafe-eval“, ist aber etwas
|
||||
* ganz anderes: es erlaubt nur das Uebersetzen von WebAssembly, kein `eval`
|
||||
* auf JavaScript. Der Scanner (#014) braucht es, sonst lehnt Chrome den
|
||||
* QR-Leser ab. Geprueft wird deshalb auf die **Marke**, nicht auf das Wort.
|
||||
*/
|
||||
const script = teile(richtlinie).get("script-src") ?? "";
|
||||
expect(script.split(" ")).not.toContain("'unsafe-eval'");
|
||||
expect(script.split(" ")).toContain("'wasm-unsafe-eval'");
|
||||
});
|
||||
|
||||
it("erlaubt keine WebSocket-Verbindungen", () => {
|
||||
@@ -38,7 +46,9 @@ describe("Ausgelieferter Stand", () => {
|
||||
});
|
||||
|
||||
it("bindet Skripte an die Nonce dieser Anfrage", () => {
|
||||
expect(teile(richtlinie).get("script-src")).toBe(`'self' 'nonce-${NONCE}' 'strict-dynamic'`);
|
||||
expect(teile(richtlinie).get("script-src")).toBe(
|
||||
`'self' 'nonce-${NONCE}' 'strict-dynamic' 'wasm-unsafe-eval'`,
|
||||
);
|
||||
});
|
||||
|
||||
it("verbietet das Einbetten in fremde Seiten und Objekte", () => {
|
||||
@@ -58,7 +68,7 @@ describe("Entwicklungsmodus", () => {
|
||||
const richtlinie = sicherheitsrichtlinie(NONCE, true);
|
||||
|
||||
it("erlaubt unsafe-eval, sonst hydriert React nicht", () => {
|
||||
expect(teile(richtlinie).get("script-src")).toContain("'unsafe-eval'");
|
||||
expect(teile(richtlinie).get("script-src")?.split(" ")).toContain("'unsafe-eval'");
|
||||
});
|
||||
|
||||
it("erlaubt die WebSocket-Verbindung von Fast Refresh", () => {
|
||||
@@ -130,7 +140,10 @@ describe("Die Entscheidung im Proxy selbst", () => {
|
||||
proxy(anfrageMitCookie()).headers.get("Content-Security-Policy"),
|
||||
);
|
||||
expect(richtlinie).toBeTruthy();
|
||||
expect(richtlinie).not.toContain("unsafe-eval");
|
||||
// Auf die Marke geprueft, nicht auf das Wort: `wasm-unsafe-eval` enthaelt es
|
||||
// und ist trotzdem keine Lockerung fuer JavaScript (#014).
|
||||
expect(richtlinie).not.toContain(" 'unsafe-eval'");
|
||||
expect(richtlinie).toContain("'wasm-unsafe-eval'");
|
||||
expect(richtlinie).toContain("connect-src 'self';");
|
||||
expect(richtlinie).toContain("'strict-dynamic'");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user