Skip to content

Ditto Web Chat – Milestone 2 Completion #4

Ditto Web Chat – Milestone 2 Completion

Ditto Web Chat – Milestone 2 Completion #4

Workflow file for this run

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}`);
}