Ditto Web Chat – Milestone 2 Completion #4
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: | |
| # Add your specific project paths here | |
| project: | |
| - "sdks/js/DittoChatCore" | |
| - "sdks/js/DittoChatUI" | |
| 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: "${{ matrix.project }}/package-lock.json" | |
| - name: Install dependencies for ${{ matrix.project }} | |
| working-directory: ${{ matrix.project }} | |
| 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 DLL files as they are not JS dependencies --> | |
| <suppress> | |
| <notes>Ignore all DLL files - not JavaScript dependencies</notes> | |
| <filePath regex="true">.*\.dll$</filePath> | |
| <cpe regex="true">.*</cpe> | |
| </suppress> | |
| <suppress> | |
| <notes>Ignore all EXE files - not JavaScript dependencies</notes> | |
| <filePath regex="true">.*\.exe$</filePath> | |
| <cpe regex="true">.*</cpe> | |
| </suppress> | |
| <suppress> | |
| <notes>Ignore all MSI files - not JavaScript dependencies</notes> | |
| <filePath regex="true">.*\.msi$</filePath> | |
| <cpe regex="true">.*</cpe> | |
| </suppress> | |
| <suppress> | |
| <notes>Ignore all SO files - not JavaScript dependencies</notes> | |
| <filePath regex="true">.*\.so$</filePath> | |
| <cpe regex="true">.*</cpe> | |
| </suppress> | |
| <suppress> | |
| <notes>Ignore all DYLIB files - not JavaScript dependencies</notes> | |
| <filePath regex="true">.*\.dylib$</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.project }}" | |
| path: "${{ matrix.project }}" | |
| format: "ALL" | |
| # Only scan JS/Node related files | |
| scan: | | |
| ${{ matrix.project }}/package.json | |
| ${{ matrix.project }}/package-lock.json | |
| ${{ matrix.project }}/node_modules | |
| args: > | |
| --enableRetired | |
| --enableExperimental | |
| --disableYarnAudit | |
| --disableNodeAudit | |
| --nodeAuditSkipDevDependencies | |
| --nodePackageSkipDevDependencies | |
| --disableAssembly | |
| --disableAutoconf | |
| --disableBundleAudit | |
| --disableCentral | |
| --disableCmake | |
| --disableCocoapodsAnalyzer | |
| --disableComposer | |
| --disableCpan | |
| --disableDart | |
| --disableGolangDep | |
| --disableGolangMod | |
| --disableJar | |
| --disableMSBuild | |
| --disableNugetconf | |
| --disableNuspec | |
| --disableOpenSSL | |
| --disablePip | |
| --disablePipfile | |
| --disablePnpmAudit | |
| --disablePoetry | |
| --disablePyDist | |
| --disablePyPkg | |
| --disableRubygems | |
| --disableSwiftPackageManagerAnalyzer | |
| --disableSwiftPackageResolvedAnalyzer | |
| --suppression suppression.xml | |
| --exclude "**/*.dll" | |
| --exclude "**/*.exe" | |
| --exclude "**/*.msi" | |
| --exclude "**/*.so" | |
| --exclude "**/*.dylib" | |
| --failOnCVSS 7 | |
| - name: Upload Dependency-Check Report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| # if: ${{ !env.ACT }} /// Check to run in local action runner | |
| with: | |
| name: dependency-check-report-${{ hashFiles(format('{0}/package.json', matrix.project)) }} | |
| path: reports/ | |
| retention-days: 15 | |
| - name: Generate Vulnerability Summary | |
| if: always() | |
| id: summary | |
| run: | | |
| PROJECT="${{ matrix.project }}" | |
| REPORT_FILE="reports/dependency-check-report.json" | |
| if [ -f "$REPORT_FILE" ]; then | |
| # Extract vulnerability counts | |
| CRITICAL=$(jq '[.dependencies[].vulnerabilities[]? | select(.severity == "CRITICAL")] | length' "$REPORT_FILE") | |
| HIGH=$(jq '[.dependencies[].vulnerabilities[]? | select(.severity == "HIGH")] | length' "$REPORT_FILE") | |
| MEDIUM=$(jq '[.dependencies[].vulnerabilities[]? | select(.severity == "MEDIUM")] | length' "$REPORT_FILE") | |
| LOW=$(jq '[.dependencies[].vulnerabilities[]? | select(.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() | |
| # if: ${{ !env.ACT }} /// Check to run in local action runner | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const project = '${{ matrix.project }}'; | |
| 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 = `${{ steps.summary.outputs.VULNERABILITIES }}`; | |
| // 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 - \`${project}\` | |
| ### ${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\` | |
| 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 - \`${project}\``) | |
| ); | |
| // 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 ${project}`); | |
| } |