mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-08 16:57:52 +08:00
修复 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)
158 lines
6.0 KiB
YAML
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('自动修复完成,已重新触发“质量守卫”工作流。');
|