Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 44 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# .github/workflows/codeql.yml
# CodeQL — dataflow / taint analysis. Free for public repos.
# Complementary to Semgrep (pattern-based). Together: comprehensive SAST coverage.
name: CodeQL

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "0 9 * * 1" # Mondays 09:00 UTC

concurrency:
group: codeql-${{ github.ref }}
cancel-in-progress: true

jobs:
analyze:
name: Analyze (javascript-typescript)
runs-on: ubuntu-latest
timeout-minutes: 360
permissions:
actions: read
contents: read
security-events: write

steps:
- uses: actions/checkout@v6

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: javascript-typescript
# Default queries only — security-extended adds noise.
queries: security-and-quality

- name: Autobuild
uses: github/codeql-action/autobuild@v4

- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:javascript-typescript"
25 changes: 17 additions & 8 deletions .github/workflows/semgrep.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# .github/workflows/semgrep.yml
# Semgrep — pattern-based SAST on the free community ruleset.
# `semgrep ci` exits non-zero on findings, so this job GATES merges on its own.
# We don't upload SARIF to the GitHub Security tab: code scanning requires
# GitHub Advanced Security on private repos, which this repo doesn't have.
# The exit-code gate is the security control; the Security-tab UI is just a
# (paid) dashboard we forgo.
# `semgrep ci` exits non-zero on findings (the merge gate); results also upload
# to the Security tab as SARIF. Code scanning is free again now the repo is
# public.
name: Semgrep

on:
Expand All @@ -28,6 +26,10 @@ jobs:
image: semgrep/semgrep
permissions:
contents: read
security-events: write
# upload-sarif reads the workflow run for context; without this the SARIF
# upload 403s with "Resource not accessible by integration".
actions: read

steps:
- uses: actions/checkout@v6
Expand All @@ -40,14 +42,21 @@ jobs:
--config p/nextjs \
--config p/react \
--config p/owasp-top-ten \
--config p/secrets
--config p/secrets \
--sarif \
--output=semgrep.sarif
env:
# No SEMGREP_APP_TOKEN — free community rulesets only. Findings fail
# the job (and print in the log); no Security-tab upload.
# No SEMGREP_APP_TOKEN — free community rulesets only.
SEMGREP_RULES: >-
p/javascript
p/typescript
p/nextjs
p/react
p/owasp-top-ten
p/secrets

- name: Upload SARIF to GitHub Security
if: always()
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: semgrep.sarif
1 change: 0 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ This is a combined RMS (Resource Management System) backend + marketing site for

### Known Issues

- **Repo is private on a free plan — CI scanning constraints:** GitHub code scanning (CodeQL + SARIF upload to the Security tab) requires paid GitHub Advanced Security on private repos, so it's unavailable. CodeQL was removed (`codeql.yml` deleted). Semgrep still runs and **gates merges via its exit code** (no SARIF upload). Classic branch protection / required status checks are also a Pro/public-only feature, so they're not API-enforceable here — rely on green CI + admin discipline when merging. Current free security gates: gitleaks (secrets), semgrep (SAST exit-code), npm audit (deps), tsc + vitest. Going public again would restore CodeQL + the Security-tab dashboards + enforceable branch protection at no cost.
- **Testimonials placeholder:** Homepage has "We're new. Testimonials are earned, not invented." — remove once real testimonials exist, or remove entirely (identified as liability in competitive review)
- **Admin password:** seed now generates a random 24-char password on first run, writes to `.first-admin-credentials` (0600). No more hardcoded `changeme123`. Founder rotated production password manually — confirm by reading the file or by attempting login with the old default (should fail)
- **No CSRF on public API routes:** `/api/tickets/reply` and `/api/files/upload` use POST but no CSRF token — relies on SameSite cookies which is sufficient for browser clients but not for API-style access
Expand Down
Loading