From 0a6902a7aaf24f4dadbe43f89e48798cb83bf89c Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:03:15 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=ED=8F=BC=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C=20=EB=B2=84=ED=8A=BC=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EA=B8=B0=EB=B3=B8=20disabled=20=EC=86=8D=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20aria-disabled=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=A0=91=EA=B7=BC=EC=84=B1=20=ED=96=A5?= =?UTF-8?q?=EC=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/palette.md | 4 ++++ app.js | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.jules/palette.md b/.jules/palette.md index 0bbf5248..0bd5df68 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -115,3 +115,7 @@ ## $(date +%Y-%m-%d) - Prevent accidental data loss in inline editors **Learning:** Forms that take a long time to fill out (like a WBS editor) are prone to accidental closure by users pressing `Escape` or clicking cancel. This causes immediate data loss without any warning, resulting in frustration. **Action:** When working on editors that can be dismissed, track whether the user has modified any fields compared to their initial state. If there are changes, intercept the close action and present a confirmation dialog (`window.confirm`) to ensure they really want to discard their edits. Bypass this for intentional saves or explicit data overrides. + +## 2026-07-14 - Replace native disabled with aria-disabled for form submit buttons +**Learning:** Native `disabled` attributes on submit buttons prevent keyboard navigation and click events, causing accessibility issues. Users tabbing through the page skip the element entirely, and the button cannot provide inline feedback when clicked. +**Action:** Use `aria-disabled="true"` instead of `disabled` for interactive buttons when the UI should preserve focusability or show inline feedback. Keep CSS in mind to style `[aria-disabled="true"]` buttons correctly (e.g., lower opacity, not-allowed cursor). Ensure form logic respects the `aria-disabled` attribute and shows appropriate feedback instead of blindly saving invalid state. diff --git a/app.js b/app.js index b8c62279..a267a5a4 100644 --- a/app.js +++ b/app.js @@ -1047,7 +1047,11 @@ function renderEditorValidation() { const saveButton = form.querySelector('button[type="submit"]'); if (saveButton) { - saveButton.disabled = errors.length > 0; + if (errors.length > 0) { + saveButton.setAttribute('aria-disabled', 'true'); + } else { + saveButton.removeAttribute('aria-disabled'); + } saveButton.title = errors.length > 0 ? '입력값을 올바르게 수정해야 저장할 수 있습니다.' : '저장 (Enter)'; } @@ -1219,6 +1223,7 @@ function saveEditor() { if (errors.length > 0) { state.editor.errors = errors; renderEditorValidation(); + showToast('입력값을 올바르게 수정해야 저장할 수 있습니다.'); return; } From a2bf6c1e8fafe1171d0b64fae74b8c791c0ffc1c Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:19:54 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20?= =?UTF-8?q?=EB=8F=99=EC=A0=81=20RegExp=EB=A5=BC=20=EB=AC=B8=EC=9E=90?= =?UTF-8?q?=EC=97=B4=20=ED=8C=8C=EC=8B=B1=EC=9C=BC=EB=A1=9C=20=EA=B5=90?= =?UTF-8?q?=EC=B2=B4=ED=95=98=EC=97=AC=20ReDoS=20=EC=B7=A8=EC=95=BD?= =?UTF-8?q?=EC=A0=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/sentinel.md | 4 ++++ cloud-sync.js | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 17f338fe..8f1d588a 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -128,3 +128,7 @@ **Vulnerability:** The backend CSV export for audit logs neutralized `=`, `+`, `-`, and `@` but failed to neutralize `|` (pipe) characters, allowing potential DDE (Dynamic Data Exchange) injection if exported logs were opened in spreadsheet software. **Learning:** Spreadsheet formula defenses must cover all command-style prefixes including `|` across all CSV export boundaries, both frontend and backend. **Prevention:** Update the sanitization regex in the backend export function to `/^[=+\-@|]/` so that all potentially executable spreadsheet payloads are prefixed with a single quote. + +## 2026-07-14 - Replace dynamic RegExp with string methods for parsed inputs +**Learning:** `RegExp` objects built from untrusted or arbitrarily complex inputs (like XML/HTML tags) can be vulnerable to Regular Expression Denial of Service (ReDoS) if the input creates backtracking paths. Security scanners like Semgrep will flag dynamic instantiation of `RegExp` objects. +**Action:** When extracting simple patterns like tag blocks where the boundaries are deterministic, prefer string manipulation functions (`indexOf`, `substring`) over dynamic `RegExp` construction. This eliminates the ReDoS risk entirely and resolves SAST warnings. diff --git a/cloud-sync.js b/cloud-sync.js index 7e44932b..a9a8c577 100644 --- a/cloud-sync.js +++ b/cloud-sync.js @@ -740,8 +740,14 @@ function openReportModal() { // hand-edited files ever matter. export function parseMsProjectXml(xml) { const tag = (block, name) => { - const m = block.match(new RegExp(`<${name}>([^<]*)`)); - return m ? m[1].trim() : ''; + const openTag = `<${name}>`; + const closeTag = ``; + const start = block.indexOf(openTag); + if (start === -1) return ''; + const contentStart = start + openTag.length; + const end = block.indexOf(closeTag, contentStart); + if (end === -1) return ''; + return block.substring(contentStart, end).trim(); }; const unescape = (s) => s .replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') From e1e2377718ac7d47bb2795bb6b1708b185bea6a8 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:45:18 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20?= =?UTF-8?q?=EB=8F=99=EC=A0=81=20RegExp=EB=A5=BC=20=EB=AC=B8=EC=9E=90?= =?UTF-8?q?=EC=97=B4=20=ED=8C=8C=EC=8B=B1=EC=9C=BC=EB=A1=9C=20=EA=B5=90?= =?UTF-8?q?=EC=B2=B4=ED=95=98=EC=97=AC=20ReDoS=20=EC=B7=A8=EC=95=BD?= =?UTF-8?q?=EC=A0=90=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20CI=20=ED=86=B5?= =?UTF-8?q?=EA=B3=BC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20pnpm=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/palette.md | 4 ++++ index.html | 2 ++ package.json | 1 + 3 files changed, 7 insertions(+) diff --git a/.jules/palette.md b/.jules/palette.md index 0bd5df68..8ca79a09 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -119,3 +119,7 @@ ## 2026-07-14 - Replace native disabled with aria-disabled for form submit buttons **Learning:** Native `disabled` attributes on submit buttons prevent keyboard navigation and click events, causing accessibility issues. Users tabbing through the page skip the element entirely, and the button cannot provide inline feedback when clicked. **Action:** Use `aria-disabled="true"` instead of `disabled` for interactive buttons when the UI should preserve focusability or show inline feedback. Keep CSS in mind to style `[aria-disabled="true"]` buttons correctly (e.g., lower opacity, not-allowed cursor). Ensure form logic respects the `aria-disabled` attribute and shows appropriate feedback instead of blindly saving invalid state. + +## 2026-07-14 - Fix CI coverage pnpm runner missing packageManager constraint +**Learning:** For continuous integration pipelines verifying static analysis and evidence gates, `pnpm` will strictly enforce that a `packageManager` key (e.g. `"packageManager": "pnpm@10.30.3"`) is declared in `package.json` to avoid mutable toolchain dependencies. +**Action:** When working in repositories using `pnpm` under strict coverage/CI environments, include `"packageManager": "pnpm@"` in `package.json` to prevent pipeline failures related to package runner resolution. diff --git a/index.html b/index.html index a7f4b49c..cda50f78 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,8 @@ ScopeWeave Planner + + diff --git a/package.json b/package.json index 9ae8b292..7c120ccb 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "type": "module", "description": "Production-grade pure HTML/CSS/JS WBS planner", + "packageManager": "pnpm@10.30.3", "scripts": { "check:python-docstrings": "node scripts/ci/static_coverage_evidence.mjs docstrings", "coverage": "node scripts/ci/static_coverage_evidence.mjs coverage && npm run test:fuzz",