Compare commits

...

4 Commits

Author SHA1 Message Date
Christian Krakau-Louis 16449e75a2 Clarify DOM XSS example in sentinel note 2026-05-27 07:11:06 +02:00
google-labs-jules[bot] d82000ab7f 🛡️ Sentinel: Fix CI failures and update dependencies
- Resolved the GitHub Actions CI deprecation warning by upgrading checkout/setup-python actions to support Node.js 24 (`v4.2.2` and `v5.4.0` respectively).
- Addressed the `pip-audit` failure (CVE-2026-4750) triggered by the malicious `fastar` dependency injection in `fastapi==0.136.3` by explicitly blocking that version (`fastapi[all]!=0.136.3`) in `requirements.txt`.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-05-27 03:44:10 +00:00
google-labs-jules[bot] 2b092592cb 🛡️ Sentinel: Fix CI failures and update dependencies
- Resolved the GitHub Actions CI deprecation warning by upgrading checkout/setup-python actions to support Node.js 24 (`v4.2.2` and `v5.4.0` respectively).
- Addressed the `pip-audit` failure (CVE-2026-4750) triggered by the malicious `fastar` dependency injection in `fastapi==0.136.3` by explicitly blocking that version (`fastapi[all]!=0.136.3`) in `requirements.txt`.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-05-27 03:38:24 +00:00
google-labs-jules[bot] 116b04ddc1 🛡️ Sentinel: [HIGH] Fix XSS in upload.js
Added an `_escapeHtml` function to properly sanitize `file.name` before it is dynamically rendered into the DOM using `row.innerHTML`. This mitigates a DOM-based Cross-Site Scripting (XSS) vulnerability.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-05-27 03:19:52 +00:00
7 changed files with 102 additions and 86 deletions
+14 -14
View File
@@ -27,9 +27,9 @@ jobs:
name: Ruff Lint & Format name: Ruff Lint & Format
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4.2.2
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.11" python-version: "3.11"
cache: 'pip' cache: 'pip'
@@ -48,9 +48,9 @@ jobs:
name: Alembic Migration Chain Check name: Alembic Migration Chain Check
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4.2.2
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.11" python-version: "3.11"
- name: Validate migration chain - name: Validate migration chain
@@ -60,9 +60,9 @@ jobs:
name: HTML Accessibility Lint name: HTML Accessibility Lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4.2.2
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.11" python-version: "3.11"
cache: 'pip' cache: 'pip'
@@ -78,9 +78,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [lint] needs: [lint]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4.2.2
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.11" python-version: "3.11"
cache: 'pip' cache: 'pip'
@@ -93,9 +93,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [lint] needs: [lint]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4.2.2
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.11" python-version: "3.11"
cache: 'pip' cache: 'pip'
@@ -116,10 +116,10 @@ jobs:
ports: ["5672:5672", "15672:15672"] ports: ["5672:5672", "15672:15672"]
options: --health-cmd "rabbitmq-diagnostics -q ping" --health-interval 10s --health-timeout 5s --health-retries 5 options: --health-cmd "rabbitmq-diagnostics -q ping" --health-interval 10s --health-timeout 5s --health-retries 5
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4.2.2
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.11" python-version: "3.11"
cache: 'pip' cache: 'pip'
@@ -154,7 +154,7 @@ jobs:
if: github.event_name == 'push' if: github.event_name == 'push'
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v4 uses: actions/checkout@v4.2.2
- name: Generate Build Metadata - name: Generate Build Metadata
run: | run: |
chmod +x scripts/generate_build_metadata.sh chmod +x scripts/generate_build_metadata.sh
@@ -212,7 +212,7 @@ jobs:
echo "tag=main-${SHORT_SHA}" >> "$GITHUB_OUTPUT" echo "tag=main-${SHORT_SHA}" >> "$GITHUB_OUTPUT"
echo "image=ghcr.io/${{ github.repository_owner }}/docuelevate:main-${SHORT_SHA}" >> "$GITHUB_OUTPUT" echo "image=ghcr.io/${{ github.repository_owner }}/docuelevate:main-${SHORT_SHA}" >> "$GITHUB_OUTPUT"
- name: Checkout k8s-cluster-state - name: Checkout k8s-cluster-state
uses: actions/checkout@v4 uses: actions/checkout@v4.2.2
with: with:
repository: christianlouis/k8s-cluster-state repository: christianlouis/k8s-cluster-state
token: ${{ secrets.GH_PAT }} token: ${{ secrets.GH_PAT }}
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
build-mode: none build-mode: none
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4.2.2
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v4 uses: github/codeql-action/init@v4
+2 -2
View File
@@ -23,13 +23,13 @@ jobs:
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v4 uses: actions/checkout@v4.2.2
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5.4.0
with: with:
python-version: '3.11' python-version: '3.11'
cache: 'pip' cache: 'pip'
+2 -2
View File
@@ -28,13 +28,13 @@ jobs:
steps: steps:
- name: Checkout PR branch - name: Checkout PR branch
uses: actions/checkout@v4 uses: actions/checkout@v4.2.2
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.head_ref }}
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.11" python-version: "3.11"
+4
View File
@@ -28,3 +28,7 @@
**Vulnerability:** The `/process-url` endpoint used `httpx.AsyncClient(follow_redirects=True)` after validating the initial user-provided URL against SSRF protections. However, it did not validate the target URLs of any subsequent HTTP redirects, allowing an attacker to provide a safe URL that redirects to an internal/private IP, bypassing the security check. **Vulnerability:** The `/process-url` endpoint used `httpx.AsyncClient(follow_redirects=True)` after validating the initial user-provided URL against SSRF protections. However, it did not validate the target URLs of any subsequent HTTP redirects, allowing an attacker to provide a safe URL that redirects to an internal/private IP, bypassing the security check.
**Learning:** Initial URL validation is insufficient when the HTTP client is configured to follow redirects automatically. The client must be explicitly configured to validate every redirect target. **Learning:** Initial URL validation is insufficient when the HTTP client is configured to follow redirects automatically. The client must be explicitly configured to validate every redirect target.
**Prevention:** When using `httpx.AsyncClient(follow_redirects=True)` for user-provided URLs, always implement a redirect validator hook function (e.g., using `event_hooks={'response': [validate_redirect]}`) that resolves the `Location` header and passes it through the same SSRF validation logic before the redirect is followed. **Prevention:** When using `httpx.AsyncClient(follow_redirects=True)` for user-provided URLs, always implement a redirect validator hook function (e.g., using `event_hooks={'response': [validate_redirect]}`) that resolves the `Location` header and passes it through the same SSRF validation logic before the redirect is followed.
## 2026-05-27 - DOM-based XSS in upload.js
**Vulnerability:** The `upload.js` frontend file directly injected the user-controlled `file.name` into a string literal passed to `row.innerHTML`. This allowed a malicious filename (e.g., `<img src=x onerror=alert(1)>`) to execute arbitrary JavaScript in the victim's browser context (DOM-based XSS).
**Learning:** Even internal or local files uploaded by a user can contain malicious filenames. Any data dynamically interpolated into an HTML string that is subsequently rendered via `innerHTML` must be strictly sanitized.
**Prevention:** Implement and apply an HTML escaping function (e.g., `_escapeHtml`) to replace dangerous characters (`&`, `<`, `>`, `"`, `'`) with their corresponding HTML entities before injecting user-controlled data into the DOM.
+13 -1
View File
@@ -293,13 +293,25 @@ function processFiles(files, progressContainer, statusMessage) {
updateStatus(); updateStatus();
} }
function _escapeHtml(unsafe) {
if (!unsafe) return '';
return String(unsafe)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
// Pre-create one progress row per file. // Pre-create one progress row per file.
const queueItems = fileArray.map((file) => { const queueItems = fileArray.map((file) => {
const row = document.createElement('div'); const row = document.createElement('div');
row.className = 'flex flex-col mb-2'; row.className = 'flex flex-col mb-2';
const safeFileName = _escapeHtml(file.name);
row.innerHTML = ` row.innerHTML = `
<div class="flex justify-between"> <div class="flex justify-between">
<span class="text-sm truncate" title="${file.name}">${file.name}</span> <span class="text-sm truncate" title="${safeFileName}">${safeFileName}</span>
<span class="text-xs text-gray-500">${formatFileSize(file.size)}</span> <span class="text-xs text-gray-500">${formatFileSize(file.size)}</span>
</div> </div>
<div class="w-full bg-gray-200 h-2 rounded-full mt-1"> <div class="w-full bg-gray-200 h-2 rounded-full mt-1">
+1 -1
View File
@@ -1,4 +1,4 @@
fastapi[all] # Web framework with all extras fastapi[all]!=0.136.3 # Web framework with all extras
uvicorn # ASGI server uvicorn # ASGI server
celery # Task queue celery # Task queue
redis # Message broker for Celery redis # Message broker for Celery