diff --git a/.github/scripts/npm-audit.sh b/.github/scripts/npm-audit.sh new file mode 100755 index 0000000..4194204 --- /dev/null +++ b/.github/scripts/npm-audit.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +dirs=() +while IFS= read -r path; do + dirs+=("$(dirname "$path")") +done < <( + find . \( -path '*/node_modules' -o -path '*/dist' \) -prune -o \ + \( -name package.json -o -name package-lock.json \) -print +) + +IFS=$'\n' dirs=($(printf '%s\n' "${dirs[@]}" | sort -u)); unset IFS + +declare -a failed=() + +for dir in "${dirs[@]}"; do + printf '\n\n\033[1;34m==> %s\033[0m\n' "$dir" + + pushd "$dir" >/dev/null + if ! npm audit --audit-level moderate; then + failed+=("$dir") + fi + popd >/dev/null +done + +if ((${#failed[@]})); then + echo -e "\n\033[0;31mnpm audit reported vulnerabilities in:\033[0m" + printf ' - %s\n' "${failed[@]}" + echo "You can run 'npm audit fix' in these directories to resolve the issues." + echo "If running 'npm audit fix' does not resolve the issues, you may need to manually update dependencies." + exit 1 +else + echo "npm audit passed: no vulnerabilities detected" + exit 0 +fi diff --git a/.github/workflows/npm-audit.yml b/.github/workflows/npm-audit.yml new file mode 100644 index 0000000..bec68eb --- /dev/null +++ b/.github/workflows/npm-audit.yml @@ -0,0 +1,67 @@ +name: "Reminder for 'run npm audit'" + +on: + schedule: + - cron: '0 22 * * *' + workflow_dispatch: + push: + branches: + - 'master' + +jobs: + run-npm-audit: + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + if: github.repository == 'line/line-bot-mcp-server' + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 + with: + node-version: '24' + + - name: Run npm audit and check diff + id: audit + run: ./scripts/npm-audit.sh + continue-on-error: true + + - name: Create or update reminder issue + if: steps.audit.outcome == 'failure' + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + TZ: 'Asia/Tokyo' + with: + script: | + const { owner, repo } = context.repo; + const title = 'Reminder: run npm audit'; + const securityURL = `https://github.com/${owner}/${repo}/security`; + const baseBody = [ + 'Fix all vulnerabilities. You can check with `.github/scripts/npm-audit.sh` locally, then send a PR with the fixes.', + `After fixing, make sure the vulnerabilities count in **${securityURL}** is **0**.` + ].join('\n\n'); + + const { data: result } = await github.rest.search.issuesAndPullRequests({ + q: `repo:${owner}/${repo} is:issue is:open in:title "${title}"` + }); + + const today = new Date(); + + if (result.total_count === 0) { + await github.rest.issues.create({ + owner, + repo, + title, + body: `${baseBody}\n\n0 days have passed.` + }); + } else { + const issue = result.items[0]; + const created = new Date(issue.created_at); + const diffDays = Math.floor((today - created) / 86_400_000); + await github.rest.issues.update({ + owner, + repo, + issue_number: issue.number, + body: `${baseBody}\n\n${diffDays} days have passed.` + }); + }