Compare commits
No commits in common. "9974948b54d5375a5a0698a134ebc928fdab0150" and "0ee9e4dd38b00952f65e3a820260a353d2478e1f" have entirely different histories.
9974948b54
...
0ee9e4dd38
4 changed files with 22 additions and 96 deletions
|
|
@ -818,8 +818,6 @@ Open security points (state at adoption, 2026-10-15; updated 2026-10-17):
|
|||
`project-state-snapshot.model.ts`, `Sidebar.tsx`, `cable-sizing-modal.tsx`,
|
||||
`estimate-balance-format.ts`, `room-area.ts` – check for ReDoS when inputs can be long;
|
||||
several `react-hooks/exhaustive-deps` warnings in the editor.
|
||||
- Baseline-Stand 2026-10-27 (security-baseline 3a23c7a): `mutation_check.py` zählt Syntax-/Importfehler nicht mehr als getötet (`[UNGÜLTIG]`), Sicherheitstests verlangen konfigurierte ungültige Eingaben (Leerlauf-Sperre).
|
||||
- Abhängigkeiten 2026-10-28: `npm audit fix` ohne `--force` (nur Lockfile) gegen die High-Funde: body-parser: 2.3.0->2.4.0, iconv-lite: 0.7.2->0.7.3, zod: 4.4.1->4.6.7. Moderat offen (Fix nur per `--force`/Major): esbuild uuid .
|
||||
|
||||
## Response Style for Codex
|
||||
|
||||
|
|
|
|||
26
package-lock.json
generated
26
package-lock.json
generated
|
|
@ -3009,18 +3009,18 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.4.0.tgz",
|
||||
"integrity": "sha512-Ifk1KcLrxaGt1occQagQNFNNOqRt1LFuehEnHHzB8Z6cRgIxRU1TBaiIsUiCdA+gy5PL7Y1IX31DNKkRmxtXjQ==",
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz",
|
||||
"integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "^3.1.2",
|
||||
"content-type": "^2.1.0",
|
||||
"content-type": "^2.0.0",
|
||||
"debug": "^4.4.3",
|
||||
"http-errors": "^2.0.1",
|
||||
"iconv-lite": "^0.7.3",
|
||||
"iconv-lite": "^0.7.2",
|
||||
"on-finished": "^2.4.1",
|
||||
"qs": "^6.16.1",
|
||||
"qs": "^6.15.2",
|
||||
"raw-body": "^3.0.2",
|
||||
"type-is": "^2.1.0"
|
||||
},
|
||||
|
|
@ -4938,10 +4938,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz",
|
||||
"integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==",
|
||||
"license": "MIT",
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
|
||||
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
|
|
@ -8481,10 +8480,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.6.7",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz",
|
||||
"integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==",
|
||||
"license": "MIT",
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.1.tgz",
|
||||
"integrity": "sha512-a6ENMBBGZBsnlSebQ/eKCguSBeGKSf4O7BPnqVPmYGtpBYI7VSqoVqw+QcB7kPRjbqPwhYTpFbVj/RqNz/CT0Q==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,6 @@ Baut gezielt je eine Schutzfunktion aus (Textersetzung), lässt die Tests laufen
|
|||
erwartet ROT. Danach wird die Datei immer wiederhergestellt. Überlebt eine Mutation
|
||||
(Tests bleiben grün), prüfen die Tests diese Schutzfunktion nicht wirklich.
|
||||
|
||||
Getötet ist eine Mutation nur, wenn ein Test fehlschlägt (pytest FAILED, unittest
|
||||
FAIL, node:test ✖ bzw. TAP "not ok") und im Fehlerbericht kein Ladefehler steht
|
||||
(Syntax-/Importfehler, siehe LOAD_ERRORS). Rot durch kaputten Code zählt als UNGÜLTIG.
|
||||
|
||||
Aufruf: mutation_check.py <mutations.toml>
|
||||
Exit: 0 = jede Mutation hat mindestens einen Test rot gemacht, 1 = sonst
|
||||
|
||||
|
|
@ -27,7 +23,6 @@ Format (Pfade relativ zur TOML-Datei):
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess # nosec B404 - Tests starten ist der Zweck des Skripts
|
||||
import sys
|
||||
import tomllib
|
||||
|
|
@ -58,70 +53,16 @@ def _write(target: Path, text: str) -> None:
|
|||
pyc.unlink(missing_ok=True)
|
||||
|
||||
|
||||
# "not ok ... # TODO|SKIP": erwartet scheiternder Test (z. B. idorGap), Lauf bleibt grün
|
||||
TAP_DIRECTIVE = re.compile(r"#\s*(TODO|SKIP)\b")
|
||||
|
||||
|
||||
def _tap_fail(stripped: str) -> bool:
|
||||
return stripped.startswith("not ok ") and not TAP_DIRECTIVE.search(stripped)
|
||||
|
||||
|
||||
def _failed_lines(output: str) -> list[str]:
|
||||
# pytest: "FAILED ...", unittest: "FAIL: ...", node:test: "✖ <Testname>" ohne
|
||||
# Zusammenfassung bzw. TAP (ältere Node-Versionen, `tsx --test` ohne Terminal):
|
||||
# "not ok <n> - <Testname>"
|
||||
def _failed_lines(stdout: str) -> list[str]:
|
||||
# pytest: "FAILED ...", node:test: "✖ <Testname>" (ohne Suiten und Zusammenfassung)
|
||||
lines = []
|
||||
for line in output.splitlines():
|
||||
stripped = line.strip()
|
||||
node_fail = stripped.startswith("✖") and "failing tests" not in line
|
||||
node_fail = node_fail or _tap_fail(stripped)
|
||||
# "FAILED (errors=1)" ist die Zusammenfassung von unittest, kein Testfehler.
|
||||
pytest_fail = line.startswith("FAILED ") and not line.startswith("FAILED (")
|
||||
if pytest_fail or line.startswith("FAIL:") or node_fail:
|
||||
lines.append(stripped)
|
||||
for line in stdout.splitlines():
|
||||
node_fail = line.startswith(" ✖") and "failing tests" not in line
|
||||
if line.startswith("FAILED") or node_fail:
|
||||
lines.append(line.strip())
|
||||
return lines
|
||||
|
||||
|
||||
# Rot durch kaputten Code ist kein Testbeleg (2026-10-27): pytest meldet einen
|
||||
# Ladefehler als ERROR ohne FAILED, node:test bricht alle Tests ab und markiert sie
|
||||
# trotzdem mit ✖.
|
||||
LOAD_ERRORS = (
|
||||
"SyntaxError",
|
||||
"IndentationError",
|
||||
"TabError",
|
||||
"ImportError",
|
||||
"ModuleNotFoundError",
|
||||
"error during collection",
|
||||
"TransformError",
|
||||
"ERR_MODULE_NOT_FOUND",
|
||||
"Cannot find module",
|
||||
"Cannot find package",
|
||||
)
|
||||
|
||||
|
||||
def _load_error(text: str) -> str | None:
|
||||
return next((marker for marker in LOAD_ERRORS if marker in text), None)
|
||||
|
||||
|
||||
def _failure_report(output: str, failed: list[str]) -> str:
|
||||
# Nur der Fehlerbericht, nicht der ganze Lauf: Logs der App sind kein Ladefehler
|
||||
# (Selftest 2026-10-27: Express loggt den JSON-SyntaxError von body-parser beim
|
||||
# Test mit "{kaputt"). node:test: Abschnitt "✖ failing tests:", TAP: die YAML-Blöcke
|
||||
# unter "not ok" (App-Logs stehen als "# ..." außerhalb), pytest/unittest: die
|
||||
# Fehlerzeilen selbst.
|
||||
_, sep, tail = output.partition("✖ failing tests:")
|
||||
if sep:
|
||||
return tail
|
||||
tap, in_block = [], False
|
||||
for line in output.splitlines():
|
||||
if _tap_fail(line.strip()):
|
||||
in_block = True
|
||||
if in_block:
|
||||
tap.append(line)
|
||||
in_block = line.strip() != "..."
|
||||
return "\n".join(tap) if tap else "\n".join(failed)
|
||||
|
||||
|
||||
def run(config_path: Path) -> int:
|
||||
base = config_path.parent
|
||||
config = tomllib.loads(config_path.read_text(encoding="utf-8"))
|
||||
|
|
@ -154,24 +95,14 @@ def run(config_path: Path) -> int:
|
|||
result = _run_tests(test_cmd, workdir)
|
||||
finally:
|
||||
_write(target, original)
|
||||
output = result.stdout + result.stderr # unittest schreibt auf stderr
|
||||
failed = _failed_lines(output)
|
||||
load_error = _load_error(_failure_report(output, failed))
|
||||
if result.returncode == 0:
|
||||
print(f"[ÜBERLEBT] {name}: Tests bleiben grün")
|
||||
survivors.append(name)
|
||||
elif load_error or not failed:
|
||||
# Ohne Fehlerzeile (pytest: nur ERROR beim Sammeln): Grund aus ganzem Lauf
|
||||
load_error = load_error or _load_error(output)
|
||||
why = "kein Testfehler erkennbar"
|
||||
if load_error:
|
||||
why = f"Ladefehler ({load_error})"
|
||||
print(f"[UNGÜLTIG] {name}: Tests rot, aber {why}, kein Testbeleg.")
|
||||
print(" Mutation so ändern, dass der Code lauffähig bleibt:")
|
||||
print(output[-800:])
|
||||
survivors.append(name)
|
||||
else:
|
||||
print(f"[GETÖTET] {name}: {failed[0]}")
|
||||
failed = _failed_lines(result.stdout)
|
||||
hint = "Tests rot, Ursache prüfen (Syntaxfehler?):\n"
|
||||
reason = failed[0] if failed else hint + result.stdout[-800:]
|
||||
print(f"[GETÖTET] {name}: {reason}")
|
||||
|
||||
print(f"\n{len(mutations) - len(survivors)}/{len(mutations)} Mutationen erkannt.")
|
||||
return 1 if survivors else 0
|
||||
|
|
|
|||
|
|
@ -132,7 +132,6 @@ describe("Eingaben", () => {
|
|||
});
|
||||
|
||||
it("ungültige Eingaben führen nie zu 5xx", async () => {
|
||||
assert.ok(config.invalidInputCases.length > 0, "invalidInputCases konfigurieren");
|
||||
const obj = await config.createOwnedObject(baseUrl, owner);
|
||||
const failures: string[] = [];
|
||||
for (const c of config.invalidInputCases) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue