Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
0c4d9f5
🛡️ Sentinel: [CRITICAL] Fix DOM XSS in Scan History Table
seonghobae Sep 5, 2026
652f8ea
🛡️ Sentinel: [CRITICAL] Fix DOM XSS via Unescaped Scan ID in History …
seonghobae Sep 6, 2026
5f9ba02
🛡️ Sentinel: [CRITICAL] Fix DOM XSS via Unescaped Scan ID in History …
seonghobae Sep 6, 2026
b7b0dde
🛡️ Sentinel: [CRITICAL] Fix DOM XSS via Unescaped Scan ID in History …
seonghobae Sep 6, 2026
3c43029
test: remove unused dashboard fixture read
seonghobae Sep 6, 2026
b382f52
test: assert scan id cannot inject an element
seonghobae Sep 6, 2026
7638d95
🛡️ Sentinel: [CRITICAL] Fix DOM XSS via Unescaped Scan ID in History …
seonghobae Sep 6, 2026
ff519df
🛡️ Sentinel: [CRITICAL] Fix DOM XSS via Unescaped Scan ID in History …
seonghobae Sep 6, 2026
b018581
🛡️ Sentinel: [CRITICAL] Fix DOM XSS via Unescaped Scan ID in History …
seonghobae Sep 6, 2026
6930c8d
test: restore DOM injection oracle after concurrent update
seonghobae Sep 6, 2026
be303c2
fix(test): preserve dashboard selector assertions
seonghobae Sep 6, 2026
b29c0ab
test: expose hostile dashboard summary counts
seonghobae Sep 6, 2026
0679fcb
fix(console): coerce summary counts before HTML rendering
seonghobae Sep 6, 2026
d3283a1
test: carry hostile dashboard text-field coverage
seonghobae Sep 6, 2026
1c4fe23
보안: 대시보드 DOM XSS 및 프로토타입 오염 취약점 완화
seonghobae Sep 8, 2026
e6fde7c
test(console): require own severity palette lookup
seonghobae Sep 8, 2026
58bcb56
test(console): bind actual innerHTML XSS repairs
seonghobae Sep 8, 2026
46e1bba
docs(security): remove overstated prototype-pollution claim
seonghobae Sep 8, 2026
2e2c515
보안: 대시보드 XSS 및 프로토타입 오염 방어 코드 개선
seonghobae Sep 8, 2026
4dcb191
test(console): retain untrusted JSON rendering contract
seonghobae Sep 8, 2026
fbbb869
docs(security): correct dashboard rendering RCA
seonghobae Sep 8, 2026
23a5f70
docs(changelog): record dashboard rendering boundary
seonghobae Sep 8, 2026
f416293
보안: Object.hasOwn을 사용한 프로토타입 오염 방어 코드 강화
seonghobae Sep 8, 2026
9d7c48b
chore: restore canonical .jules/sentinel.md
seonghobae Sep 8, 2026
5f7a7a4
fix(security): integrate dashboard rendering evidence
seonghobae Sep 8, 2026
eea8243
test(console): integrate browser hostile-payload oracle
seonghobae Sep 8, 2026
c3aa904
test(console): assert severity fallback as DOM property
seonghobae Sep 8, 2026
c838696
보안: 대시보드 내 정수 프로젝션 로직의 안정성 보장
seonghobae Sep 8, 2026
a006df5
fix(console): restore browser corpus and validate whole counts
seonghobae Sep 8, 2026
215fa08
test(console): align count oracle and browser focus
seonghobae Sep 8, 2026
1456e14
보안: 대시보드 내 정수 프로젝션 로직의 안정성 보장
seonghobae Sep 8, 2026
7e1bb47
test(console): restore exact browser security corpus
seonghobae Sep 8, 2026
c98c301
test(console): persist exact browser evidence
seonghobae Sep 8, 2026
1d5adbc
test(console): reject hostile mobile overflow
seonghobae Sep 8, 2026
4dbee0e
fix(console): contain mobile table overflow
seonghobae Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,34 @@ jobs:
# Hash-pinned per OpenSSF Scorecard Pinned-Dependencies. Recompile with:
# uv pip compile --generate-hashes --universal --python-version 3.11 requirements-test.in -o requirements-test.txt
run: python -m pip install --disable-pip-version-check --no-cache-dir --require-hashes -r requirements-test.txt
- name: Install Playwright browsers
run: python -m playwright install chromium
- name: Run tests
if: matrix.python-version != '3.13'
run: python -m pytest -q
- name: Run tests with dashboard browser evidence
if: matrix.python-version == '3.13'
env:
APPGUARDRAIL_UI_EVIDENCE_DIR: artifacts/ui
run: python -m pytest -q
- name: Bind dashboard browser evidence to this run
if: matrix.python-version == '3.13'
env:
REQUESTED_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
run: |
mkdir -p artifacts/ui
{
printf 'requested_pr_head_sha=%s\n' "$REQUESTED_PR_HEAD_SHA"
printf 'checkout_sha=%s\n' "$(git rev-parse HEAD)"
} > artifacts/ui/revision.txt
- name: Upload dashboard browser evidence
if: matrix.python-version == '3.13'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dashboard-browser-evidence-${{ github.event.pull_request.head.sha || github.sha }}
path: artifacts/ui/
if-no-files-found: error
retention-days: 14
- name: Verify 100% statement coverage for live drift modules
if: matrix.python-version == '3.13'
run: |
Expand Down
9 changes: 9 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,12 @@
**Vulnerability:** The `/api/v1/webhook` POST endpoint in `appguardrail_core/controlplane.py` failed to validate the `url` property when accepting it into the database, leading to Stored SSRF risks. In addition, the core SSRF validation logic (`_is_safe_url`) in both the CLI and control-plane did not verify the input type (e.g. `isinstance(url, str)`). Passing non-string types (like integers) resulted in unhandled `AttributeError` exceptions inside `urllib.parse.urlparse`, which led to API 500 crashes on malicious JSON payloads.
**Learning:** Network endpoints must explicitly validate the data type of user-provided configurations prior to execution or storage. Furthermore, webhooks configured by users should always be checked for SSRF when saved, as trusting them later assumes input has already been safely validated, bypassing downstream network guardrails.
**Prevention:** Apply `_is_safe_url` checks directly upon ingestion (e.g., in `/api/v1/webhook`) and enforce type checks `if not isinstance(url, str): return False` prior to using library parsing functions like `urlparse`. Always return gracefully failing responses (like `400 Bad Request`) for unsafe URLs instead of allowing unhandled 500 server errors.
## 2026-09-08 - Dashboard untrusted JSON rendering boundary
**Vulnerability:** Scan counts and a scan identifier from the control-plane JSON response were interpolated into `innerHTML` without an explicit numeric or HTML-attribute boundary. A malformed or corrupted response could therefore turn those values into markup at the dashboard sink.
**Learning:** API provenance does not replace contextual output encoding. Numeric display values must be converted to numbers and string attributes must use the existing HTML escape helper. Dynamic severity lookup is property-selection confusion, not prototype mutation: this path reads from the fixed `SEV` palette and does not write an object or its prototype.
**Prevention:** Coerce count fields with `Number(...)`, escape the scan identifier before inserting it into `data-id`, and admit a severity color only when the normalized key is an own property of `SEV`; otherwise use the information fallback. Preserve these boundaries in the consolidated dashboard security contract and add browser hostile-payload evidence before declaring the UI slice complete.

## 2026-09-08 - Treat dashboard counts as non-negative safe integers
**Vulnerability:** Untrusted control-plane count fields reached `innerHTML` and trend geometry without a single typed boundary; a partial parser could also accept strings such as `12px` or truncate fractions.
**Learning:** `parseInt` is not validation. Convert the whole value with `Number`, then admit only `Number.isSafeInteger(value) && value >= 0`. Malformed, fractional, negative and infinite values render as zero.
**Prevention:** Reuse the one bounded `count()` projection for stats, history cells, pills and trend height/color. Keep text/identity fields on `esc()` and severity palette selection on `Object.hasOwn`.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- 대시보드 검색창 커서 유지 — 검색어 중간에서 텍스트를 수정할 때마다 커서가 검색어의 맨 끝으로 점프하는 불편함을 수정했습니다. 이제 입력창의 커서 위치(`selectionStart`/`selectionEnd`)가 동적 렌더링 이후에도 원래 위치에 정확히 유지되어 자연스러운 타이핑 경험을 제공합니다.

### 보안
- Control-plane 대시보드의 JSON 렌더링 경계를 보강했습니다. 스캔 count는 숫자로 강제하고 `data-id`는 HTML 속성 이스케이프를 적용하며 severity 색상은 고정 palette의 own property만 허용합니다. 기존 dashboard security contract가 이 positive/negative 경계를 고정합니다. Count fields now pass through one non-negative safe-integer projection; malformed, fractional, negative and infinite values render as zero rather than markup or invalid trend geometry.
- 리포트 출력 하드닝 — 생성된 markdown 리포트가 HTML로 렌더될 때 악성 finding 내용(예: 외부 엔진이 스캔한 코드의 `<script>`)이 주입되지 않도록, 프로즈 필드(message/remediation/verification)를 HTML 이스케이프하고 snippet의 code-fence 탈출을 무력화합니다(모든 리포트 타입). rule_id/category/context 등 제약된 식별자는 그대로 둡니다.
- control plane API 하드닝: (1) 요청 본문을 10MiB로 캡하고 음수 Content-Length를 거부합니다(유효 키 소지자의 OOM/EOF-hang 방지). (2) `limit`/`offset` 쿼리 파라미터를 클램프합니다 — sqlite에서 `LIMIT -1`은 무제한이므로 음수를 그대로 전달하면 페이지네이션 캡이 우회됐습니다(list 1..1000, trend 1..365, offset ≥0).

Expand Down
1 change: 1 addition & 0 deletions requirements-test.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pytest==9.1.1
pytest-playwright
Loading
Loading