From f76e832ae019717d5e7f3ad81a3fb421586ab679 Mon Sep 17 00:00:00 2001 From: datarian Date: Sat, 13 Jun 2026 08:23:00 +0200 Subject: [PATCH 1/3] fix(ci): replace job-level hashFiles() with step-based file check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hashFiles() is not available in job-level if conditions — only step-level. Replace with a checkout + bash file-existence check that sets a step output, then gate the remaining npm-audit steps on that output. Also add the required glob pattern to the step-level hashFiles call in the semgrep job. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/security.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 8e7823b..c5a8b5d 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -33,21 +33,30 @@ jobs: npm-audit: name: npm Dependency Audit runs-on: ubuntu-latest - # Skip gracefully if the web-builder hasn't been committed yet - if: ${{ hashFiles('resumes/web-builder/package.json') != '' }} steps: - uses: actions/checkout@v4 + - name: Check if web-builder exists + id: check-webbuilder + run: | + if [ -f "resumes/web-builder/package.json" ]; then + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "exists=false" >> $GITHUB_OUTPUT + echo "::notice::resumes/web-builder/package.json not found — skipping npm audit" + fi - uses: actions/setup-node@v4 + if: steps.check-webbuilder.outputs.exists == 'true' with: node-version: '20' cache: npm cache-dependency-path: resumes/web-builder/package-lock.json - name: Install dependencies + if: steps.check-webbuilder.outputs.exists == 'true' working-directory: resumes/web-builder run: npm ci - name: Audit for vulnerabilities (moderate+) + if: steps.check-webbuilder.outputs.exists == 'true' working-directory: resumes/web-builder - # --audit-level=moderate fails on moderate, high, and critical issues run: npm audit --audit-level=moderate # ────────────────────────────────────────────────────────────── @@ -68,7 +77,7 @@ jobs: # --- TypeScript / React (standard auto rules) --- - name: Semgrep – TypeScript/React auto rules - if: ${{ hashFiles('resumes/web-builder/src') != '' }} + if: ${{ hashFiles('resumes/web-builder/src/**') != '' }} # Exit 0 so we always upload SARIF; pipeline gating is done via SARIF review run: | semgrep scan \ From c05ab7c9842a581cf53741c072980e94e77526e5 Mon Sep 17 00:00:00 2001 From: datarian Date: Sat, 13 Jun 2026 08:26:36 +0200 Subject: [PATCH 2/3] fix(ci): fix invalid semgrep rule syntax (exit code 7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two rules had patterns semgrep cannot parse: - llm08-child-process-exec-dynamic: "..." inside a string literal is not valid semgrep ellipsis syntax — remove pattern-not, flag all exec() calls - llm02-dangerous-set-inner-html: standalone JSX attribute pattern is unreliable to parse — switch to pattern-regex on generic language Co-Authored-By: Claude Sonnet 4.6 --- .github/semgrep/llm-owasp.yaml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/semgrep/llm-owasp.yaml b/.github/semgrep/llm-owasp.yaml index 12672ad..cc0ed6a 100644 --- a/.github/semgrep/llm-owasp.yaml +++ b/.github/semgrep/llm-owasp.yaml @@ -33,12 +33,12 @@ rules: cwe: CWE-79 - id: llm02-dangerous-set-inner-html - pattern: dangerouslySetInnerHTML={{ __html: $VAL }} + pattern-regex: 'dangerouslySetInnerHTML\s*=\s*\{\{' message: > LLM02 (Insecure Output Handling): dangerouslySetInnerHTML renders raw HTML, bypassing React's XSS protection. Sanitize AI-generated content with DOMPurify before passing it here, or restructure to avoid raw HTML rendering. - languages: [typescript, javascript] + languages: [generic] severity: WARNING metadata: owasp-llm: LLM02 @@ -71,17 +71,12 @@ rules: owasp-llm: LLM08 cwe: CWE-95 - - id: llm08-child-process-exec-dynamic - patterns: - - pattern: | - require('child_process').exec($CMD, ...) - - pattern-not: | - require('child_process').exec("...", ...) + - id: llm08-child-process-exec + pattern: require('child_process').exec($CMD, ...) message: > - LLM08 (Excessive Agency): child_process.exec() with a dynamic command - string can enable OS command injection if $CMD contains AI-generated or - user-controlled content. Use execFile() with a fixed binary and argument - array instead. + LLM08 (Excessive Agency): child_process.exec() passes the command to a + shell, enabling command injection if $CMD is AI-generated or user-controlled. + Use execFile() with a fixed binary and an argument array instead. languages: [typescript, javascript] severity: ERROR metadata: From 425ee156bb879351e77cc901c4dddb986a13fa41 Mon Sep 17 00:00:00 2001 From: datarian Date: Sat, 13 Jun 2026 08:27:43 +0200 Subject: [PATCH 3/3] fix(ci): upgrade codeql-action upload-sarif from v3 to v4 v3 is deprecated in December 2026. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/security.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index c5a8b5d..df94907 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -89,7 +89,7 @@ jobs: - name: Upload TypeScript SARIF if: always() && hashFiles('semgrep-ts.sarif') != '' - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: semgrep-ts.sarif category: semgrep-typescript @@ -106,7 +106,7 @@ jobs: - name: Upload LLM OWASP SARIF if: always() && hashFiles('semgrep-llm.sarif') != '' - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: semgrep-llm.sarif category: semgrep-llm-owasp