APP-951-New architecture for ditto #72
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: OWASP Dependency Check | |
| on: | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| security-events: write | |
| jobs: | |
| dependency-check: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| # Workspace package names | |
| workspace: | |
| - "@dittolive/ditto-chat-core" | |
| - "@dittolive/ditto-chat-ui" | |
| include: | |
| - workspace: "@dittolive/ditto-chat-core" | |
| path: "sdks/js/ditto-chat-core" | |
| - workspace: "@dittolive/ditto-chat-ui" | |
| path: "sdks/js/ditto-chat-ui" | |
| fail-fast: false | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| cache-dependency-path: "package-lock.json" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Create suppression file | |
| run: | | |
| cat > suppression.xml << 'EOF' | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd"> | |
| <!-- Suppress non-Node.js files: binaries, CocoaPods, Gradle, Xcode --> | |
| <suppress> | |
| <filePath regex="true">.*((node_modules.*\.(dll|exe|so|dylib|msi))|\.podspec|/Podfile(\.lock)?|/Pods/.*|\.gradle|/gradlew(\.bat)?|\.(xcodeproj|xcworkspace)/.*)$</filePath> | |
| <cpe regex="true">.*</cpe> | |
| </suppress> | |
| </suppressions> | |
| EOF | |
| - name: Run OWASP Dependency-Check | |
| uses: dependency-check/Dependency-Check_Action@main | |
| id: depcheck | |
| with: | |
| project: "${{ matrix.workspace }}" | |
| path: "${{ matrix.path }}" | |
| format: "ALL" | |
| args: > | |
| -s "${{ matrix.path }}/package.json" | |
| -s "package-lock.json" | |
| --enableExperimental | |
| --suppression suppression.xml | |
| --failOnCVSS 7 | |
| - name: Upload Dependency-Check Report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: dependency-check-report-${{ hashFiles(format('{0}/package.json', matrix.path)) }} | |
| path: reports/ | |
| retention-days: 15 | |
| - name: Generate Vulnerability Summary | |
| if: always() | |
| id: summary | |
| run: | | |
| WORKSPACE="${{ matrix.workspace }}" | |
| REPORT_FILE="reports/dependency-check-report.json" | |
| if [ -f "$REPORT_FILE" ]; then | |
| # Extract vulnerability counts (handle both uppercase and lowercase severity values) | |
| CRITICAL=$(jq '[.dependencies[].vulnerabilities[]? | select(.severity == "CRITICAL" or .severity == "critical")] | length' "$REPORT_FILE") | |
| HIGH=$(jq '[.dependencies[].vulnerabilities[]? | select(.severity == "HIGH" or .severity == "high")] | length' "$REPORT_FILE") | |
| MEDIUM=$(jq '[.dependencies[].vulnerabilities[]? | select(.severity == "MEDIUM" or .severity == "moderate")] | length' "$REPORT_FILE") | |
| LOW=$(jq '[.dependencies[].vulnerabilities[]? | select(.severity == "LOW" or .severity == "low")] | length' "$REPORT_FILE") | |
| TOTAL=$(jq '[.dependencies[].vulnerabilities[]?] | length' "$REPORT_FILE") | |
| echo "critical=$CRITICAL" >> $GITHUB_OUTPUT | |
| echo "high=$HIGH" >> $GITHUB_OUTPUT | |
| echo "medium=$MEDIUM" >> $GITHUB_OUTPUT | |
| echo "low=$LOW" >> $GITHUB_OUTPUT | |
| echo "total=$TOTAL" >> $GITHUB_OUTPUT | |
| # Extract detailed vulnerabilities | |
| echo "VULNERABILITIES<<EOF" >> $GITHUB_OUTPUT | |
| jq -r '.dependencies[] | select(.vulnerabilities) | .fileName as $file | .vulnerabilities[] | "- **\(.name)** (\(.severity)) in `\($file)`\n - Description: \(.description // "N/A" | gsub("[`\\n\\r\"$]"; " "))\n - CVSS: \(.cvssv3.baseScore // .cvssv2.score // "N/A")\n"' "$REPORT_FILE" | head -n 50 >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| else | |
| echo "critical=0" >> $GITHUB_OUTPUT | |
| echo "high=0" >> $GITHUB_OUTPUT | |
| echo "medium=0" >> $GITHUB_OUTPUT | |
| echo "low=0" >> $GITHUB_OUTPUT | |
| echo "total=0" >> $GITHUB_OUTPUT | |
| echo "VULNERABILITIES=No vulnerabilities found" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Comment PR with Detailed Results | |
| if: always() | |
| uses: actions/github-script@v7 | |
| env: | |
| VULN_DATA: ${{ steps.summary.outputs.VULNERABILITIES }} | |
| with: | |
| script: | | |
| const workspace = '${{ matrix.workspace }}'; | |
| const critical = '${{ steps.summary.outputs.critical }}'; | |
| const high = '${{ steps.summary.outputs.high }}'; | |
| const medium = '${{ steps.summary.outputs.medium }}'; | |
| const low = '${{ steps.summary.outputs.low }}'; | |
| const total = '${{ steps.summary.outputs.total }}'; | |
| const vulnerabilities = process.env.VULN_DATA; | |
| // Determine status emoji and message | |
| let statusEmoji = '✅'; | |
| let statusMessage = 'No vulnerabilities found'; | |
| if (parseInt(critical) > 0 || parseInt(high) > 0) { | |
| statusEmoji = '🚨'; | |
| statusMessage = 'Critical or High severity vulnerabilities detected!'; | |
| } else if (parseInt(medium) > 0) { | |
| statusEmoji = '⚠️'; | |
| statusMessage = 'Medium severity vulnerabilities detected'; | |
| } else if (parseInt(low) > 0) { | |
| statusEmoji = 'ℹ️'; | |
| statusMessage = 'Low severity vulnerabilities detected'; | |
| } | |
| const body = `## ${statusEmoji} OWASP Dependency Check - \`${workspace}\` | |
| ### ${statusMessage} | |
| | Severity | Count | | |
| |----------|------:| | |
| | 🔴 Critical | **${critical}** | | |
| | 🟠 High | **${high}** | | |
| | 🟡 Medium | **${medium}** | | |
| | 🔵 Low | **${low}** | | |
| | **Total** | **${total}** | | |
| ${parseInt(total) > 0 ? ` | |
| ### 📋 Vulnerability Details | |
| ${vulnerabilities} | |
| ${parseInt(total) > 50 ? '_Note: Showing first 50 vulnerabilities. Check the full report for complete details._' : ''} | |
| ` : ''} | |
| --- | |
| 📊 **[View Full HTML Report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})** | |
| <details> | |
| <summary>ℹ️ How to fix vulnerabilities</summary> | |
| 1. Update vulnerable dependencies to patched versions | |
| 2. Run \`npm audit fix\` or \`npm audit fix --force\` in the root directory | |
| 3. Check for alternative packages if updates aren't available | |
| 4. Review and update your \`package.json\` and \`package-lock.json\` | |
| </details> | |
| `; | |
| // Find existing comment | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const botComment = comments.find(comment => | |
| comment.user.type === 'Bot' && | |
| comment.body.includes(`OWASP Dependency Check - \`${workspace}\``) | |
| ); | |
| // Update or create comment | |
| if (botComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: botComment.id, | |
| body: body | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: body | |
| }); | |
| } | |
| // Fail the job if critical or high vulnerabilities found | |
| if (parseInt(critical) > 0 || parseInt(high) > 0) { | |
| core.setFailed(`Found ${critical} critical and ${high} high severity vulnerabilities in ${workspace}`); | |
| } |