Skip to content

feat(admin): add get group detail info feature for admin #60

feat(admin): add get group detail info feature for admin

feat(admin): add get group detail info feature for admin #60

name: Security – React/Vite + Spring (SAST & SCA)
on:
pull_request:
push:
branches: [ main ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
security:
runs-on: ubuntu-latest
timeout-minutes: 50
permissions:
contents: read
security-events: write
pull-requests: write
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install jq (for SARIF checks)
run: |
sudo apt-get update -y
sudo apt-get install -y jq
# ──────────────────────────────
# Node (프론트엔드가 있을 때만)
# ──────────────────────────────
- name: Use Node
if: ${{ hashFiles('**/package-lock.json', '**/npm-shrinkwrap.json', '**/pnpm-lock.yaml', '**/yarn.lock') != '' }}
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies (no scripts)
if: ${{ hashFiles('**/package-lock.json', '**/npm-shrinkwrap.json', '**/pnpm-lock.yaml', '**/yarn.lock') != '' }}
run: npm ci --ignore-scripts --no-audit
# ──────────────────────────────
# Semgrep (SAST)
# ──────────────────────────────
- name: Set up Python (for Semgrep CLI)
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Semgrep
run: |
python -m pip install --upgrade pip
pip install semgrep
- name: Run Semgrep (OWASP + Java/Kotlin) → SARIF
run: |
set -euo pipefail
semgrep --version
semgrep ci \
--config p/owasp-top-ten \
--config p/java \
--config p/kotlin \
--sarif -o semgrep.sarif || true
- name: Run Semgrep (JS/React) → append SARIF
if: ${{ hashFiles('**/package-lock.json', '**/npm-shrinkwrap.json', '**/pnpm-lock.yaml', '**/yarn.lock') != '' }}
run: |
set -euo pipefail
semgrep ci \
--config p/javascript \
--config p/react \
--sarif -o semgrep-js.sarif || true
jq -s '.[0].runs += .[1].runs | .[0]' semgrep.sarif semgrep-js.sarif > semgrep-merged.sarif && mv semgrep-merged.sarif semgrep.sarif
- name: Check Semgrep SARIF has results
id: semgrep_sarif_check
run: |
if [ ! -s semgrep.sarif ]; then
echo "ok=false" >> $GITHUB_OUTPUT; exit 0
fi
runs=$(jq '.runs | length' semgrep.sarif 2>/dev/null || echo 0)
results=$(jq '[.runs[].results] | flatten | length' semgrep.sarif 2>/dev/null || echo 0)
if [ "$runs" -gt 0 ] && [ "$results" -gt 0 ]; then echo "ok=true" >> $GITHUB_OUTPUT; else echo "ok=false" >> $GITHUB_OUTPUT; fi
- name: Upload Semgrep SARIF
if: ${{ steps.semgrep_sarif_check.outputs.ok == 'true' }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: semgrep.sarif
# ──────────────────────────────
# Node SCA — npm audit (report-only)
# ──────────────────────────────
- name: Node audit (npm)
if: ${{ hashFiles('**/package-lock.json', '**/npm-shrinkwrap.json', '**/pnpm-lock.yaml', '**/yarn.lock') != '' }}
run: npm audit --audit-level=high || true
# ──────────────────────────────
# Java build (Maven/Gradle 자동 감지)
# ──────────────────────────────
- name: Setup Temurin JDK
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
- name: Build (Maven)
if: ${{ hashFiles('**/pom.xml') != '' }}
run: mvn -B -DskipTests package
- name: Setup Gradle
if: ${{ hashFiles('**/pom.xml') == '' && (hashFiles('**/build.gradle') != '' || hashFiles('**/build.gradle.kts') != '') }}
uses: gradle/actions/setup-gradle@v4
- name: Build (Gradle)
if: ${{ hashFiles('**/pom.xml') == '' && (hashFiles('**/build.gradle') != '' || hashFiles('**/build.gradle.kts') != '') }}
run: ./gradlew build -x test
# ──────────────────────────────
# Dependency-Check (Java SCA) → SARIF
# ──────────────────────────────
- name: OWASP Dependency-Check → SARIF (fast, Java-only)
if: ${{ hashFiles('**/pom.xml', '**/build.gradle*') != '' }}
uses: dependency-check/Dependency-Check_Action@main
env:
JAVA_HOME: /opt/jdk # action 이미지 요구사항
with:
project: ${{ github.repository }}
path: .
format: 'SARIF'
out: 'dependency-check-report'
args: >
--noupdate
--disableCentral
--disableNodeJS --disableNodeAudit --disableYarnAudit --disablePnpmAudit --disableRetireJS
--enableRetired
--failOnCVSS 11
--suppression .github/dependency-check-suppressions.xml
--log dependency-check-report/odc.log
continue-on-error: true
- name: Upload Dependency-Check SARIF
if: ${{ hashFiles('dependency-check-report/dependency-check-report.sarif') != '' }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: dependency-check-report/dependency-check-report.sarif
- name: Upload Dependency-Check log (always)
if: always()
uses: actions/upload-artifact@v4
with:
name: dependency-check-log
path: dependency-check-report/odc.log
# ──────────────────────────────
# Trivy (filesystem scan) → SARIF
# ──────────────────────────────
- name: Trivy filesystem scan
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: fs
ignore-unfixed: true
severity: HIGH,CRITICAL
format: sarif
output: trivy-fs.sarif
exit-code: '0'
- name: Check Trivy FS SARIF has results
id: trivyfs_sarif_check
run: |
if [ ! -s trivy-fs.sarif ]; then
echo "ok=false" >> $GITHUB_OUTPUT; exit 0
fi
runs=$(jq '.runs | length' trivy-fs.sarif 2>/dev/null || echo 0)
results=$(jq '[.runs[].results] | flatten | length' trivy-fs.sarif 2>/dev/null || echo 0)
if [ "$runs" -gt 0 ] && [ "$results" -gt 0 ]; then echo "ok=true" >> $GITHUB_OUTPUT; else echo "ok=false" >> $GITHUB_OUTPUT; fi
- name: Upload Trivy FS SARIF
if: ${{ steps.trivyfs_sarif_check.outputs.ok == 'true' }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-fs.sarif
# ──────────────────────────────
# Hadolint
# ──────────────────────────────
- name: Locate Dockerfile(s)
id: df
shell: bash
run: |
set -euo pipefail
mapfile -t files < <(find . -type f \( -iname "dockerfile" -o -iname "*.dockerfile" \) | sort || true)
if [ ${#files[@]} -eq 0 ]; then
echo "found=false" >> $GITHUB_OUTPUT
echo "count=0" >> $GITHUB_OUTPUT
echo "No Dockerfile found. Skipping hadolint."
exit 0
fi
printf '%s\n' "${files[@]}" > dockerfiles.list
echo "found=true" >> $GITHUB_OUTPUT
echo "count=${#files[@]}" >> $GITHUB_OUTPUT
- name: Run hadolint (container CLI)
if: ${{ steps.df.outputs.found == 'true' }}
shell: bash
run: |
set -euo pipefail
mapfile -t files < dockerfiles.list
echo "Linting: ${files[*]}"
docker run --rm -v "$PWD":/work -w /work hadolint/hadolint:latest \
hadolint --format sarif "${files[@]}" > hadolint.sarif || true
test -s hadolint.sarif || echo '{"version":"2.1.0","runs":[]}' > hadolint.sarif
ls -l hadolint.sarif
- name: Upload Hadolint SARIF
if: ${{ steps.df.outputs.found == 'true' }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: hadolint.sarif
# ──────────────────────────────
# (optional) Build local image → Trivy image scan
# ──────────────────────────────
- name: Build image (local only)
if: ${{ steps.df.outputs.found == 'true' }}
uses: docker/build-push-action@v6
with:
context: .
push: false
tags: local/lms-back:secscan
- name: Trivy image scan
if: ${{ steps.df.outputs.found == 'true' }}
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: image
image-ref: local/lms-back:secscan
ignore-unfixed: true
severity: HIGH,CRITICAL
format: sarif
output: trivy-image.sarif
exit-code: '0'
- name: Check Trivy IMG SARIF has results
if: ${{ steps.df.outputs.found == 'true' }}
id: trivyimg_sarif_check
run: |
if [ ! -s trivy-image.sarif ]; then
echo "ok=false" >> $GITHUB_OUTPUT; exit 0
fi
runs=$(jq '.runs | length' trivy-image.sarif 2>/dev/null || echo 0)
results=$(jq '[.runs[].results] | flatten | length' trivy-image.sarif 2>/dev/null || echo 0)
if [ "$runs" -gt 0 ] && [ "$results" -gt 0 ]; then echo "ok=true" >> $GITHUB_OUTPUT; else echo "ok=false" >> $GITHUB_OUTPUT; fi
- name: Upload Trivy IMG SARIF
if: ${{ steps.df.outputs.found == 'true' && steps.trivyimg_sarif_check.outputs.ok == 'true' }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-image.sarif
# ──────────────────────────────
# Gitleaks (Secrets)
# ──────────────────────────────
- name: Run Gitleaks (secrets scan) → SARIF
shell: bash
run: |
set -euo pipefail
docker run --rm -v "$PWD":/repo ghcr.io/gitleaks/gitleaks:v8.18.4 detect \
--source=/repo \
--redact \
--report-format sarif \
--report-path /repo/gitleaks.sarif \
--exit-code 0
test -s gitleaks.sarif || echo '{"version":"2.1.0","runs":[]}' > gitleaks.sarif
ls -l gitleaks.sarif
- name: Check Gitleaks SARIF has results
id: gitleaks_sarif_check
run: |
if [ ! -s gitleaks.sarif ]; then
echo "ok=false" >> $GITHUB_OUTPUT; exit 0
fi
runs=$(jq '.runs | length' gitleaks.sarif 2>/dev/null || echo 0)
results=$(jq '[.runs[].results] | flatten | length' gitleaks.sarif 2>/dev/null || echo 0)
if [ "$runs" -gt 0 ] && [ "$results" -gt 0 ]; then echo "ok=true" >> $GITHUB_OUTPUT; else echo "ok=false" >> $GITHUB_OUTPUT; fi
- name: Upload Gitleaks SARIF
if: ${{ steps.gitleaks_sarif_check.outputs.ok == 'true' }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: gitleaks.sarif