Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2593292
AMP-31141 : Re-enable and redesign CSRF protection for state-changing…
brianbrix Jun 15, 2026
b604a60
AMP-31141 : Failing maven build
brianbrix Jun 15, 2026
afcafc4
AMP-31141 : Re-enable and redesign CSRF protection for state-changing…
brianbrix Jun 15, 2026
c3f64d8
AMP-31141 : Re-enable and redesign CSRF protection for state-changing…
brianbrix Jun 15, 2026
00da6aa
AMP-31141 : Re-enable and redesign CSRF protection for state-changing…
brianbrix Jun 16, 2026
5ecea45
AMP-31141 : Re-enable and redesign CSRF protection for state-changing…
brianbrix Jun 16, 2026
97bb146
AMP-31141 : Activity form not saving due to CSRF
brianbrix Jun 16, 2026
0a70668
AMP-31141 : Activity form not saving due to CSRF
brianbrix Jun 16, 2026
0103f69
AMP-31140 : CSRF protection on REST endpoints
brianbrix Jun 16, 2026
e465941
AMP-31140 : CSRF protection on REST endpoints
brianbrix Jun 16, 2026
e84e47d
AMP-31142 : Modernize authentication handling
brianbrix Jun 16, 2026
e5fa1c7
AMP-31143 : Restore browser hardening headers and document cookie and…
brianbrix Jun 16, 2026
14d4a62
AMP-31141 : Failing to get resources
brianbrix Jun 16, 2026
a21da26
AMP-31141 : Failing to get resources
brianbrix Jun 16, 2026
5b21037
AMP-31144 : Add dependency and CVE scanning for OWASP audit
brianbrix Jun 16, 2026
ffe9285
AMP-31144 : Add dependency and CVE scanning for OWASP audit
brianbrix Jun 16, 2026
d57eff9
AMP-31144 : Add dependency and CVE scanning for OWASP audit
brianbrix Jun 16, 2026
478e2db
AMP-31144 : Add dependency and CVE scanning for OWASP audit
brianbrix Jun 16, 2026
b1f8e11
AMP-31144 : Add dependency and CVE scanning for OWASP audit
brianbrix Jun 16, 2026
2689305
AMP-31144 : Add dependency and CVE scanning for OWASP audit
brianbrix Jun 16, 2026
af575e2
AMP-31144 : Add dependency and CVE scanning for OWASP audit
brianbrix Jun 17, 2026
edab738
AMP-31144 : Add dependency and CVE scanning for OWASP audit
brianbrix Jun 17, 2026
ab2da48
AMP-31144 : Add dependency and CVE scanning for OWASP audit
brianbrix Jun 17, 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
268 changes: 268 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
name: Security Scan

on:
push:
# Scan the default branch directly on merge; feature branches are covered
# by the pull_request trigger below, avoiding duplicate runs.
branches:
- develop
- main
pull_request:
branches: [ "**" ]
schedule:
# Re-scan every Monday at 06:00 UTC to catch newly published CVEs.
# Runs on the default branch; push/PR triggers cover all other branches.
- cron: '0 6 * * 1'
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to scan (optional — leave blank to scan the branch selected above)'
required: false
type: string
default: ''

permissions:
contents: read

jobs:
# -----------------------------------------------------------------------
# Resolve the exact commit to scan.
# - Automatic triggers (push / PR / schedule): use the triggering SHA.
# - Manual (workflow_dispatch, no pr_number): use the branch selected in
# the "Run workflow" UI — GitHub checks out that branch automatically.
# - Manual (workflow_dispatch + pr_number): fetch the PR head SHA via API.
# -----------------------------------------------------------------------
setup:
name: Resolve target ref
runs-on: ubuntu-latest
outputs:
ref: ${{ steps.resolve.outputs.ref }}
steps:
- name: Resolve checkout ref
id: resolve
run: |
PR="${{ inputs.pr_number }}"
if [ -n "$PR" ]; then
if ! [[ "$PR" =~ ^[0-9]+$ ]]; then
echo "::error::pr_number must be numeric, got: ${PR}"
exit 1
fi
DATA=$(curl -sf \
-H "Authorization: Bearer ${{ github.token }}" \
"https://api.github.com/repos/${{ github.repository }}/pulls/${PR}")
STATE=$(echo "$DATA" | jq -r '.state')
if [ "$STATE" != "open" ]; then
echo "::error::PR #${PR} is not open (state: ${STATE})"
exit 1
fi
HEAD_REF=$(echo "$DATA" | jq -r '.head.ref')
HEAD_SHA=$(echo "$DATA" | jq -r '.head.sha')
echo "Scanning PR #${PR}: branch=${HEAD_REF} sha=${HEAD_SHA}"
echo "ref=${HEAD_SHA}" >> $GITHUB_OUTPUT
else
echo "ref=${{ github.sha }}" >> $GITHUB_OUTPUT
fi

# -----------------------------------------------------------------------
# Job 1: OWASP Dependency-Check (Java / Maven artifacts)
# -----------------------------------------------------------------------
owasp-maven:
name: OWASP Dependency-Check (Maven)
runs-on: ubuntu-latest
timeout-minutes: 60
needs: setup

steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ needs.setup.outputs.ref }}

- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
cache: maven

# Cache the NVD data feed so successive runs skip the ~60s download
- name: Cache NVD data
uses: actions/cache@v4
with:
path: ~/.m2/repository/org/owasp/dependency-check-data
key: nvd-${{ runner.os }}-${{ github.run_id }}
restore-keys: |
nvd-${{ runner.os }}-

- name: Run OWASP Dependency-Check
working-directory: amp
# Call the plugin goal directly to avoid triggering the full Maven
# lifecycle (which would run frontend-maven-plugin and fail in CI).
continue-on-error: true
id: owasp
run: |
mvn -B -ntp \
org.owasp:dependency-check-maven:9.2.0:check \
-P security \
-DskipTests \
-Dmaven.test.skip=true

- name: Upload HTML report
if: always()
uses: actions/upload-artifact@v4
with:
name: owasp-dependency-check-report
path: amp/target/dependency-check-report/
retention-days: 30

- name: Fail if OWASP found CVEs
if: steps.owasp.outcome == 'failure'
run: exit 1

# -----------------------------------------------------------------------
# Job 2: npm audit — all frontend packages
# -----------------------------------------------------------------------
npm-audit:
name: npm audit (Frontend)
runs-on: ubuntu-latest
timeout-minutes: 30
needs: setup

strategy:
fail-fast: false
matrix:
package:
- path: amp/TEMPLATE/reamp
name: reamp
- path: amp/TEMPLATE/ampTemplate/amp-state
name: amp-state
- path: amp/TEMPLATE/ampTemplate/amp-boilerplate
name: amp-boilerplate
- path: amp/TEMPLATE/ampTemplate/amp-filter
name: amp-filter
- path: amp/TEMPLATE/ampTemplate/amp-translate
name: amp-translate
- path: amp/TEMPLATE/ampTemplate/amp-url
name: amp-url
- path: amp/TEMPLATE/ampTemplate/amp-settings
name: amp-settings
- path: amp/TEMPLATE/ampTemplate/gis-layers-manager
name: gis-layers-manager
- path: amp/TEMPLATE/ampTemplate/dashboard/dev
name: dashboard
- path: amp/TEMPLATE/ampTemplate/gisModule/dev
name: gisModule

steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ needs.setup.outputs.ref }}

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '16'

- name: npm install (no scripts)
working-directory: ${{ matrix.package.path }}
run: npm install --ignore-scripts --prefer-offline 2>&1 || true

- name: npm audit
id: audit
working-directory: ${{ matrix.package.path }}
# Continue so all packages are scanned; the summary step decides outcome
continue-on-error: true
run: |
npm audit \
--audit-level high \
--json \
> /tmp/${{ matrix.package.name }}-audit.json 2>&1

- name: Print audit summary
if: always()
working-directory: ${{ matrix.package.path }}
run: |
FILE="/tmp/${{ matrix.package.name }}-audit.json"
if command -v jq &>/dev/null && [ -f "$FILE" ]; then
echo "### ${{ matrix.package.name }} vulnerability counts"
jq '{
critical: (.metadata.vulnerabilities.critical // 0),
high: (.metadata.vulnerabilities.high // 0),
moderate: (.metadata.vulnerabilities.moderate // 0),
low: (.metadata.vulnerabilities.low // 0)
}' "$FILE"
fi

- name: Upload audit JSON
if: always()
uses: actions/upload-artifact@v4
with:
name: npm-audit-${{ matrix.package.name }}
path: /tmp/${{ matrix.package.name }}-audit.json
retention-days: 30

- name: Fail on HIGH or CRITICAL findings
working-directory: ${{ matrix.package.path }}
run: |
FILE="/tmp/${{ matrix.package.name }}-audit.json"
if [ ! -f "$FILE" ]; then exit 0; fi
HIGH=$(jq '.metadata.vulnerabilities.high // 0' "$FILE")
CRIT=$(jq '.metadata.vulnerabilities.critical // 0' "$FILE")
TOTAL=$(( HIGH + CRIT ))
if [ "$TOTAL" -gt 0 ]; then
echo "::error::${{ matrix.package.name }}: ${CRIT} critical + ${HIGH} high vulnerabilities found. Run 'npm audit' in ${{ matrix.package.path }} for details."
exit 1
fi

# -----------------------------------------------------------------------
# Job 3: Trivy — container image CVE scan (runs on push to main / PRs)
# Catches OS-level CVEs that OWASP cannot see (e.g. base image packages)
# -----------------------------------------------------------------------
trivy-image:
name: Trivy (Container Image)
runs-on: ubuntu-latest
timeout-minutes: 30
needs: setup
# Only scan the image on pushes to main/master or on PRs targeting them,
# to avoid redundant scans on every feature branch push.
if: |
github.event_name == 'pull_request' ||
github.ref == 'refs/heads/main' ||
github.ref == 'refs/heads/master' ||
github.event_name == 'schedule' ||
github.event_name == 'workflow_dispatch'

steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ needs.setup.outputs.ref }}

- name: Build Docker image (no push)
working-directory: amp
run: |
docker build \
--build-arg BUILD_SOURCE=security-scan \
--build-arg AMP_URL=http://localhost/ \
-t amp-security-scan:${{ github.sha }} \
. || echo "::warning::Docker build failed; skipping Trivy image scan"

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: 'amp-security-scan:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'HIGH,CRITICAL'
exit-code: '1'
ignore-unfixed: true
continue-on-error: true

- name: Upload Trivy SARIF artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: trivy-sarif
path: trivy-results.sarif
retention-days: 30
6 changes: 5 additions & 1 deletion amp/context.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
allowLinking="true" antiJARLocking="" antiResourceLocking="" clearReferencesStopTimerThreads="true"
clearReferencesThreadLocals="true" jndiExceptionOnFailedWrite="false" processTlds="false">
<Environment name="hostname" value="" type="java.lang.String"/>


<!-- SameSite=Lax: browser will not send the session cookie on cross-site
POST/PUT/DELETE requests, providing CSRF protection for all endpoints
including /rest/** without requiring any token in requests. -->
<CookieProcessor sameSiteCookies="lax" />
<!-- PostgreSQL connection -->
<Resource factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
defaultTransactionIsolation="READ_COMMITTED"
Expand Down
Loading
Loading