feat(admin): add get group detail info feature for admin #60
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |