Files
eSIM-Tools/.github/workflows/ci-auto-heal.yml
Abner a1628aa33a 🐛 fix(security): 修复全部 76 个 CodeQL 安全扫描告警
修复 GitHub 代码扫描发现的所有安全问题,涵盖 20 个文件、76 个告警:

- Actions 工作流:添加 Fork 仓库限制、来源验证、persist-credentials: false、移除 npm cache
- 明文存储:localStorage 迁移到 secureStorage (sessionStorage + TTL)
- 日志注入:新增 sanitizeLog() 过滤换行符,覆盖全部 console.log 调用
- SSRF:新增 isAllowedTarget() 域名白名单校验
- SRI 完整性:为 Bootstrap/Font Awesome/GTM 添加 integrity + crossorigin
- XSS 防护:错误消息 HTML 转义、Service Worker origin 验证
- URL 清洗:hostname.includes() 替换为精确域名匹配
- HTML 过滤:扩展标签黑名单、添加事件属性过滤
- 限流:全局引入 createRateLimiter (200 req/min/IP)
2026-05-06 21:46:25 +08:00

158 lines
6.0 KiB
YAML

name: CI 自动修复
on:
workflow_run:
workflows:
- 质量守卫
types:
- completed
permissions:
contents: read
actions: write
jobs:
auto-heal:
if: >-
${{
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.run_attempt == 1 &&
github.event.workflow_run.head_repository.full_name == github.repository
}}
runs-on: ubuntu-latest
steps:
- name: 下载质量报告
id: download
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: quality-report
path: quality-report
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: 验证报告来源
id: verify_source
if: steps.download.outcome == 'success'
run: |
RUN_INFO=$(gh api repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }} --jq '.head_repository.full_name // .head_branch')
echo "报告来源: $RUN_INFO"
if [ "$RUN_INFO" != "${{ github.repository }}" ] && [ "$RUN_INFO" != "${{ github.event.workflow_run.head_branch }}" ]; then
echo "::warning::质量报告来源不匹配,跳过自动修复"
echo "need_heal=false" >> "$GITHUB_OUTPUT"
fi
- name: 分析失败原因
id: failure_info
run: |
node <<'NODE'
const fs = require('fs');
const path = 'quality-report/quality-report.json';
let qualityFailed = false;
let securityFailed = false;
let testFailed = false;
let buildFailed = false;
let reportFound = false;
if (fs.existsSync(path)) {
reportFound = true;
const report = JSON.parse(fs.readFileSync(path, 'utf8'));
qualityFailed = report?.outcomes?.quality !== 'success';
securityFailed = report?.outcomes?.security !== 'success';
testFailed = report?.outcomes?.test !== 'success';
buildFailed = report?.outcomes?.build !== 'success';
fs.writeFileSync('auto-heal-context.json', JSON.stringify(report, null, 2));
console.log('质量报告中的运行结果:', report.outcomes);
} else {
console.log('未找到质量报告附件,无法判断失败原因。');
}
const needHeal = qualityFailed || securityFailed || testFailed || buildFailed;
const lines = [
`report_found=${reportFound ? 'true' : 'false'}`,
`need_heal=${needHeal ? 'true' : 'false'}`,
`quality_failed=${qualityFailed ? 'true' : 'false'}`,
`security_failed=${securityFailed ? 'true' : 'false'}`,
`test_failed=${testFailed ? 'true' : 'false'}`,
`build_failed=${buildFailed ? 'true' : 'false'}`
];
if (process.env.GITHUB_OUTPUT) {
fs.appendFileSync(process.env.GITHUB_OUTPUT, `${lines.join('\n')}\n`);
}
NODE
- name: 无需修复(质量报告正常)
if: steps.failure_info.outputs.need_heal != 'true'
run: echo '没有检测到可自动修复的任务,跳过此流程。'
- name: 检出目标提交
if: steps.failure_info.outputs.need_heal == 'true'
uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha }}
persist-credentials: false
- name: 安装 Node.js 环境
if: steps.failure_info.outputs.need_heal == 'true'
uses: actions/setup-node@v4
with:
node-version: '20'
- name: 重新安装依赖
if: steps.failure_info.outputs.need_heal == 'true'
run: |
if ! npm ci; then
echo '首次安装失败,改用 npm install'
npm install
fi
- name: 重跑质量检查
id: retry_quality
if: steps.failure_info.outputs.need_heal == 'true' && steps.failure_info.outputs.quality_failed == 'true'
run: npm run quality-check
- name: 重跑安全检查
id: retry_security
if: steps.failure_info.outputs.need_heal == 'true' && steps.failure_info.outputs.security_failed == 'true'
run: npm run security-check
- name: 重跑单元测试
id: retry_test
if: steps.failure_info.outputs.need_heal == 'true' && steps.failure_info.outputs.test_failed == 'true'
run: npm test
- name: 重新构建
id: retry_build
if: steps.failure_info.outputs.need_heal == 'true' && steps.failure_info.outputs.build_failed == 'true'
run: npm run build
- name: 汇总修复结果
if: steps.failure_info.outputs.need_heal == 'true'
run: |
cat <<'TXT' > auto-heal-report.txt
need_heal=${{ steps.failure_info.outputs.need_heal }}
quality_retry=${{ steps.retry_quality.outcome || 'skipped' }}
security_retry=${{ steps.retry_security.outcome || 'skipped' }}
test_retry=${{ steps.retry_test.outcome || 'skipped' }}
build_retry=${{ steps.retry_build.outcome || 'skipped' }}
TXT
- name: 上传修复结果
if: steps.failure_info.outputs.need_heal == 'true'
uses: actions/upload-artifact@v4
with:
name: auto-heal-report
path: auto-heal-report.txt
retention-days: 5
- name: 重新触发质量守卫
if: steps.failure_info.outputs.need_heal == 'true' && steps.retry_quality.outcome != 'failure' && steps.retry_security.outcome != 'failure' && steps.retry_test.outcome != 'failure' && steps.retry_build.outcome != 'failure'
uses: actions/github-script@v7
with:
script: |
const runId = ${{ github.event.workflow_run.id }};
await github.request('POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun', {
owner: context.repo.owner,
repo: context.repo.repo,
run_id: runId
});
core.notice('自动修复完成,已重新触发“质量守卫”工作流。');