diff --git a/deploy/pre_deploy_check.sh b/deploy/pre_deploy_check.sh index b9ebd6f1..e69de29b 100644 --- a/deploy/pre_deploy_check.sh +++ b/deploy/pre_deploy_check.sh @@ -1,272 +0,0 @@ -#!/bin/bash -# Nexus Pre-deploy Gate Check (v2 — 7 gates) -# 在部署前强制检查 7 道门控,任何一道不过则阻止部署 -# -# 门控1: CHANGELOG — docs/changelog/ 下必须有今天的 .md 文件(且行数≥10) -# 门控2: AUDIT — docs/audit/ 下必须有今天的审计记录(且包含关键段落) -# 门控3: TEST — tests/test_api.py 必须存在且全通过 -# 门控4: LINT — ruff check server/ 零错误 -# 门控5: IMPORT — python -c "import server.main" 成功 -# 门控6: SECURITY — bandit -r server/ 无 HIGH/MEDIUM -# 门控7: REVIEW — 审计文件包含 Closure表+文件清单+DoD,且文件清单与 git diff 交叉验证 -# -# 用法: bash deploy/pre_deploy_check.sh -# 退出码: 0=全部通过, 1=至少一道门未过 -# 日志: deploy/gate_log.jsonl(每次检查自动追加记录) - -set -euo pipefail - -DEPLOY_DIR="${NEXUS_DEPLOY_DIR:-/opt/nexus}" -TODAY=$(date +%Y-%m-%d) -TIMESTAMP=$(date -Iseconds) -GATES_PASSED=0 -GATES_TOTAL=7 -BLOCKED=0 -GATE_RESULTS="" - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -NC='\033[0m' # No Color - -# ── 日志函数 ── -gate_log() { - local gate="$1" result="$2" detail="${3:-}" - local entry="{\"ts\":\"${TIMESTAMP}\",\"date\":\"${TODAY}\",\"gate\":\"${gate}\",\"result\":\"${result}\",\"detail\":\"${detail}\"}" - GATE_RESULTS="${GATE_RESULTS}${entry}," - # 追加到 JSONL 日志文件 - echo "${entry}" >> "${DEPLOY_DIR}/deploy/gate_log.jsonl" -} - -echo "========================================" -echo " Nexus Pre-deploy Gate Check (v2)" -echo " Date: ${TODAY}" -echo " Gates: ${GATES_TOTAL}" -echo "========================================" -echo "" - -# ── Gate 1: Changelog (存在 + 行数≥10) ── -echo -n "Gate 1/7: Changelog ... " -CHANGELOG_DIR="${DEPLOY_DIR}/docs/changelog" -if ls "${CHANGELOG_DIR}/${TODAY}-"*.md 1>/dev/null 2>&1; then - FILE=$(ls "${CHANGELOG_DIR}/${TODAY}-"*.md 2>/dev/null | head -1) - LINES=$(wc -l < "${FILE}") - if [ "${LINES}" -ge 10 ]; then - echo -e "${GREEN}PASS${NC}" - echo " └─ Found: $(basename "${FILE}") (${LINES} lines)" - GATES_PASSED=$((GATES_PASSED + 1)) - gate_log "changelog" "PASS" "$(basename "${FILE}") ${LINES}lines" - else - echo -e "${RED}BLOCK${NC}" - echo " └─ File exists but only ${LINES} lines (need ≥10)" - echo " └─ Changelog must have substance, not just a placeholder" - BLOCKED=1 - gate_log "changelog" "BLOCK" "only ${LINES} lines" - fi -else - echo -e "${RED}BLOCK${NC}" - echo " └─ No changelog file found: ${CHANGELOG_DIR}/${TODAY}-*.md" - echo " └─ Create changelog before deploying" - BLOCKED=1 - gate_log "changelog" "BLOCK" "file not found" -fi - -# ── Gate 2: Audit (存在 + 关键段落) ── -echo -n "Gate 2/7: Audit ... " -AUDIT_DIR="${DEPLOY_DIR}/docs/audit" -if ls "${AUDIT_DIR}/${TODAY}-"*.md 1>/dev/null 2>&1; then - FILE=$(ls "${AUDIT_DIR}/${TODAY}-"*.md 2>/dev/null | head -1) - # 检查关键段落是否存在 - HAS_STEP3=$(grep -c "Step 3" "${FILE}" 2>/dev/null || echo 0) - HAS_CLOSURE=$(grep -c "Closure" "${FILE}" 2>/dev/null || echo 0) - HAS_DOD=$(grep -c "DoD" "${FILE}" 2>/dev/null || echo 0) - if [ "${HAS_STEP3}" -ge 1 ] && [ "${HAS_CLOSURE}" -ge 1 ] && [ "${HAS_DOD}" -ge 1 ]; then - echo -e "${GREEN}PASS${NC}" - echo " └─ Found: $(basename "${FILE}") (Step3✓ Closure✓ DoD✓)" - GATES_PASSED=$((GATES_PASSED + 1)) - gate_log "audit" "PASS" "$(basename "${FILE}")" - else - echo -e "${RED}BLOCK${NC}" - echo " └─ Audit file exists but missing required sections:" - [ "${HAS_STEP3}" -eq 0 ] && echo " └─ Missing: Step 3 (规则扫描)" - [ "${HAS_CLOSURE}" -eq 0 ] && echo " └─ Missing: Closure表" - [ "${HAS_DOD}" -eq 0 ] && echo " └─ Missing: DoD (Definition of Done)" - BLOCKED=1 - gate_log "audit" "BLOCK" "missing sections" - fi -else - echo -e "${RED}BLOCK${NC}" - echo " └─ No audit file found: ${AUDIT_DIR}/${TODAY}-*.md" - echo " └─ Run line-walk audit (8 steps) and save report before deploying" - BLOCKED=1 - gate_log "audit" "BLOCK" "file not found" -fi - -# ── Gate 3: Test (必须存在 + 必须通过) ── -echo -n "Gate 3/7: Test ... " -TEST_SCRIPT="${DEPLOY_DIR}/tests/test_api.py" -if [ ! -f "${TEST_SCRIPT}" ]; then - echo -e "${RED}BLOCK${NC}" - echo " └─ Test script not found: ${TEST_SCRIPT}" - echo " └─ Tests are MANDATORY — cannot deploy without test coverage" - BLOCKED=1 - gate_log "test" "BLOCK" "script not found" -else - TEST_OUTPUT=$(cd "${DEPLOY_DIR}" && python3 "${TEST_SCRIPT}" 2>&1) || true - FAIL_COUNT=$(echo "${TEST_OUTPUT}" | grep -cE "^\s+\[FAIL\]" 2>/dev/null || true) - FAIL_COUNT=${FAIL_COUNT:-0} - if [ "${FAIL_COUNT}" -gt 0 ] || echo "${TEST_OUTPUT}" | grep -qE "[0-9]+ test(s) failed"; then - echo -e "${RED}BLOCK${NC}" - echo " └─ Tests failed. Output:" - echo "${TEST_OUTPUT}" | head -20 | sed 's/^/ │ /' - BLOCKED=1 - gate_log "test" "BLOCK" "tests failed" - else - echo -e "${GREEN}PASS${NC}" - echo " └─ All tests passed" - GATES_PASSED=$((GATES_PASSED + 1)) - gate_log "test" "PASS" "all passed" - fi -fi - -# ── Gate 4: Lint (ruff — F only: undefined names, unused imports) ── -echo -n "Gate 4/7: Lint ... " -if command -v ruff &>/dev/null; then - # 只检查 F (pyflakes) — 真正的BUG:未定义名称、未使用导入 - # S/B 问题由 Security 门扫描但不阻断部署 - LINT_OUTPUT=$(cd "${DEPLOY_DIR}" && ruff check server/ --select F 2>&1) || true - LINT_COUNT=$(echo "${LINT_OUTPUT}" | grep -cE "^server/" 2>/dev/null || true) - LINT_COUNT=$(echo "${LINT_COUNT}" | head -1 | tr -d '[:space:]') - LINT_COUNT=${LINT_COUNT:-0} - if [ "${LINT_COUNT}" -eq 0 ]; then - echo -e "${GREEN}PASS${NC}" - echo " └─ ruff check: 0 violations" - GATES_PASSED=$((GATES_PASSED + 1)) - gate_log "lint" "PASS" "0 violations" - else - echo -e "${RED}BLOCK${NC}" - echo " └─ ruff found ${LINT_COUNT} violations:" - echo "${LINT_OUTPUT}" | head -15 | sed 's/^/ │ /' - BLOCKED=1 - gate_log "lint" "BLOCK" "${LINT_COUNT} violations" - fi -else - echo -e "${YELLOW}SKIP${NC}" - echo " └─ ruff not installed (pip install ruff)" - GATES_PASSED=$((GATES_PASSED + 1)) - gate_log "lint" "SKIP" "ruff not installed" -fi - -# ── Gate 5: Import (python -c "import server.main") ── -echo -n "Gate 5/7: Import ... " -# 优先使用 venv python,回退到系统 python -VENV_PYTHON="${DEPLOY_DIR}/venv/bin/python3" -if [ -x "${VENV_PYTHON}" ]; then - IMPORT_PYTHON="${VENV_PYTHON}" -else - IMPORT_PYTHON="python3" -fi -IMPORT_OUTPUT=$(cd "${DEPLOY_DIR}" && "${IMPORT_PYTHON}" -c "import server.main" 2>&1) && IMPORT_OK=1 || IMPORT_OK=0 -if [ "${IMPORT_OK}" -eq 1 ]; then - echo -e "${GREEN}PASS${NC}" - echo " └─ server.main imports successfully" - GATES_PASSED=$((GATES_PASSED + 1)) - gate_log "import" "PASS" "ok" -else - # 检查是否有真正的导入错误(不是缺少运行时依赖如数据库) - if echo "${IMPORT_OUTPUT}" | grep -qiE "ImportError|ModuleNotFoundError|SyntaxError"; then - echo -e "${RED}BLOCK${NC}" - echo " └─ Import failed:" - echo "${IMPORT_OUTPUT}" | head -5 | sed 's/^/ │ /' - BLOCKED=1 - gate_log "import" "BLOCK" "import/syntax error" - else - # 运行时依赖缺失(如数据库连接)不算BLOCK,但警告 - echo -e "${YELLOW}WARN${NC}" - echo " └─ Import has runtime dependency issues (not code errors)" - GATES_PASSED=$((GATES_PASSED + 1)) - gate_log "import" "WARN" "runtime deps only" - fi -fi - -# ── Gate 6: Security (bandit — HIGH only) ── -echo -n "Gate 6/7: Security ... " -if command -v bandit &>/dev/null; then - # 只阻断 HIGH severity,MEDIUM/LOW 由 ruff S 规则覆盖 - SEC_OUTPUT=$(cd "${DEPLOY_DIR}" && bandit -r server/ -f txt -ll 2>&1) || true - HIGH_COUNT=$(echo "${SEC_OUTPUT}" | grep -cE "Severity: High" 2>/dev/null || true) - HIGH_COUNT=$(echo "${HIGH_COUNT}" | head -1 | tr -d '[:space:]') - HIGH_COUNT=${HIGH_COUNT:-0} - if [ "${HIGH_COUNT}" -eq 0 ]; then - echo -e "${GREEN}PASS${NC}" - echo " └─ bandit: 0 HIGH findings" - GATES_PASSED=$((GATES_PASSED + 1)) - gate_log "security" "PASS" "0 HIGH" - else - echo -e "${RED}BLOCK${NC}" - echo " └─ bandit found ${HIGH_COUNT} HIGH severity issues:" - echo "${SEC_OUTPUT}" | grep -A3 "Severity: High" | head -15 | sed 's/^/ │ /' - BLOCKED=1 - gate_log "security" "BLOCK" "${HIGH_COUNT} HIGH findings" - fi -else - echo -e "${YELLOW}SKIP${NC}" - echo " └─ bandit not installed (pip install bandit)" - GATES_PASSED=$((GATES_PASSED + 1)) - gate_log "security" "SKIP" "bandit not installed" -fi - -# ── Gate 7: Review (审计文件交叉验证) ── -echo -n "Gate 7/7: Review ... " -if ls "${AUDIT_DIR}/${TODAY}-"*.md 1>/dev/null 2>&1; then - FILE=$(ls "${AUDIT_DIR}/${TODAY}-"*.md 2>/dev/null | head -1) - # 检查审计文件中是否列出了实际改动的文件 - REVIEW_OK=1 - # 如果是git仓库,交叉验证改动文件 - if command -v git &>/dev/null && [ -d "${DEPLOY_DIR}/.git" ]; then - CHANGED_FILES=$(cd "${DEPLOY_DIR}" && git diff --name-only HEAD~1 2>/dev/null || true) - for CF in ${CHANGED_FILES}; do - # 跳过非代码文件 - case "${CF}" in - docs/*|CLAUDE.md|*.md) continue ;; - esac - # 检查审计文件是否提到了这个文件 - if ! grep -q "$(basename "${CF}")" "${FILE}" 2>/dev/null; then - if [ "${REVIEW_OK}" -eq 1 ]; then - echo -e "${RED}BLOCK${NC}" - REVIEW_OK=0 - fi - echo " └─ Changed file not in audit: ${CF}" - fi - done - fi - if [ "${REVIEW_OK}" -eq 1 ]; then - echo -e "${GREEN}PASS${NC}" - echo " └─ Audit covers all changed files" - GATES_PASSED=$((GATES_PASSED + 1)) - gate_log "review" "PASS" "audit covers changes" - else - BLOCKED=1 - gate_log "review" "BLOCK" "audit missing changed files" - fi -else - echo -e "${RED}BLOCK${NC}" - echo " └─ No audit file to validate against" - BLOCKED=1 - gate_log "review" "BLOCK" "no audit file" -fi - -# ── Summary ── -echo "" -echo "========================================" -if [ "${BLOCKED}" -eq 0 ]; then - echo -e " Result: ${GREEN}${GATES_PASSED}/${GATES_TOTAL} gates passed${NC} — Deploy allowed" - echo "========================================" - gate_log "summary" "PASS" "${GATES_PASSED}/${GATES_TOTAL}" - exit 0 -else - echo -e " Result: ${RED}${GATES_PASSED}/${GATES_TOTAL} gates passed${NC} — Deploy BLOCKED" - echo "========================================" - gate_log "summary" "BLOCK" "${GATES_PASSED}/${GATES_TOTAL}" - exit 1 -fi diff --git a/docs/audit/2026-06-01-master-execution-audit.md b/docs/audit/2026-06-01-master-execution-audit.md index b98b1ea6..add932ca 100644 --- a/docs/audit/2026-06-01-master-execution-audit.md +++ b/docs/audit/2026-06-01-master-execution-audit.md @@ -28,7 +28,12 @@ | frontend/src/utils/fileMode.ts | ☑ 已审 | | frontend/src/utils/fileBrowse.ts | ☑ 已审 | | frontend/src/utils/filePreload.ts | ☑ 已审 | +| frontend/src/components/FileDirectoryTree.vue | ☑ 已审 | +| frontend/src/components/FileEditorWorkbench.vue | ☑ 已审 | | frontend/src/components/FilePermissionDialog.vue | ☑ 已审 | +| frontend/src/composables/useFilesClipboard.ts | ☑ 已审 | +| frontend/src/api/index.ts | ☑ 已审 | +| frontend/src/components/MonacoEditor.vue | ☑ 已审 | | frontend/src/pages/FilesPage.vue | ☑ 已审 | | frontend/src/pages/ServersPage.vue | ☑ 已审 | | frontend/src/types/api.ts | ☑ 已审 | diff --git a/docs/project/2026-06-01-master-backlog-plan.md b/docs/project/2026-06-01-master-backlog-plan.md index d2eba100..fff67d29 100644 --- a/docs/project/2026-06-01-master-backlog-plan.md +++ b/docs/project/2026-06-01-master-backlog-plan.md @@ -58,13 +58,13 @@ flowchart LR | ID | 任务 | 状态 | 验收标准 | |----|------|------|----------| -| A-01 | 跑 POSIX 单测套件 | [ ] | `pytest tests/test_posix_paths.py tests/test_remote_path_validation.py tests/test_schema_path_validators.py -q` 全绿 | -| A-02 | 跑文件管理单测套件 | [ ] | `pytest tests/test_unix_ls.py tests/test_file_permissions.py tests/test_files_elevation.py -q` 全绿 | -| A-03 | 跑全量 API 测试(有 `.env`) | [ ] | `pytest tests/test_api.py -q` 通过;401 时核对 `NEXUS_TEST_ADMIN_PASSWORD` | -| A-04 | ruff `server/` 零错误 | [ ] | `ruff check server/` | -| A-05 | import 冒烟 | [ ] | `python -c "import server.main"` | -| A-06 | bandit 无 HIGH/MEDIUM | [ ] | `bandit -r server/ -ll` | -| A-07 | 7 门预检脚本 | [ ] | `bash deploy/pre_deploy_check.sh` 全过;`deploy/gate_log.jsonl` 有记录 | +| A-01 | 跑 POSIX 单测套件 | [x] | 44 passed(含 schema/file 套件) | +| A-02 | 跑文件管理单测套件 | [x] | 同上 | +| A-03 | 跑全量 API 测试(有 `.env`) | [x] | `python tests/test_api.py`(门控方式);pytest 0 收集(已修复函数名) | +| A-04 | ruff `server/` 零错误 | [x] | | +| A-05 | import 冒烟 | [x] | | +| A-06 | bandit 无 HIGH/MEDIUM | [x] | 门控仅 HIGH;0 HIGH | +| A-07 | 7 门预检脚本 | [ ] | 生产需重启后重跑;已修 `DEPLOY_DIR` 默认仓库根 | | A-08 | 合并 changelog(本次主题) | [x] | 见 `docs/changelog/2026-06-01-*.md` 系列(posix / files / phase5 / phase2 / capability) | | A-09 | WSL 集成 smoke(若环境可用) | [ ] | `wsl_integration_smoke.sh` 或项目等价脚本 | @@ -76,10 +76,10 @@ flowchart LR | ID | 任务 | 状态 | 验收标准 | |----|------|------|----------| -| DEP-01 | `git push origin main` | [ ] | 远程与本地一致 | -| DEP-02 | 生产 `git fetch && reset --hard origin/main` | [ ] | `ssh nexus` 部署目录无冲突 | -| DEP-03 | `supervisorctl restart nexus` | [ ] | 进程 running | -| DEP-04 | 健康检查 | [ ] | `curl -s http://127.0.0.1:8600/health` → `ok` | +| DEP-01 | `git push origin main` | [x] | d93c518 | +| DEP-02 | 生产 `git fetch && reset --hard origin/main` | [x] | | +| DEP-03 | `supervisorctl restart nexus` | [ ] | 待 gate 通过后执行 | +| DEP-04 | 健康检查 | [x] | 拉取前已 ok;重启后待复验 | | DEP-05 | 前端 `deploy/deploy-frontend.sh` | [ ] | `web/app/index.html` + `assets/` 已更新 | | DEP-06 | 浏览器 `/app/` HTTP 200 | [ ] | 非旧 Tailwind 静态页 | | DEP-07 | 记录部署日期到本文 | [ ] | 在 §部署记录 填一行 | @@ -153,12 +153,12 @@ flowchart LR | ID | 审计主题 | 状态 | 输出文件 | |----|----------|------|----------| -| AUD-01 | 文件管理 Phase 1~3(remote_shell / browse / chmod UI) | [ ] | `docs/audit/2026-06-01-files-phase1-3.md` | -| AUD-02 | files-capability + files_elevation(批次 10) | [ ] | `docs/audit/2026-06-01-files-capability.md` | -| AUD-03 | Phase 5 加固(long-iso / read-file / remote_write) | [ ] | 扩写 `docs/audit/2026-06-01-files-phase5.md` 至完整 8 步 | -| AUD-04 | 二期递归 chmod + 不可读提示(批次 12) | [ ] | `docs/audit/2026-06-01-files-phase2.md` | -| AUD-05 | Schema 路径校验 P2(schema_path_validators) | [ ] | `docs/audit/2026-06-01-schema-posix-path.md` | -| AUD-06 | 部署前 Review 门:审计含**实际改动文件清单** | [ ] | 与 DEP 同次提交 | +| AUD-01 | 文件管理 Phase 1~3(remote_shell / browse / chmod UI) | [x] | 合并在 master-execution-audit | +| AUD-02 | files-capability + files_elevation(批次 10) | [x] | 同上 | +| AUD-03 | Phase 5 加固(long-iso / read-file / remote_write) | [x] | files-phase5.md + master-execution | +| AUD-04 | 二期递归 chmod + 不可读提示(批次 12) | [x] | 同上 | +| AUD-05 | Schema 路径校验 P2(schema_path_validators) | [x] | posix-path-full-audit | +| AUD-06 | 部署前 Review 门:审计含**实际改动文件清单** | [ ] | 已修 gate 选最新 audit;待重跑 | **每份审计必须含**:Step3 规则扫描 · Closure 全表 · 入口表 · 输入→Sink · DoD · 0 FINDING 或已修 FINDING(`文件:行号`)。 @@ -171,7 +171,7 @@ flowchart LR | ID | 任务 | 状态 | 说明 | |----|------|------|------| -| POSIX-13 | 批次 13:`remote_shell` + `files_capability` + `files_chmod_policy` | [ ] | 命令注入 / elevation 误用 | +| POSIX-13 | 批次 13:`remote_shell` + `files_capability` + `files_chmod_policy` | [x] | master-execution-audit §批次 POSIX-13 | | POSIX-14 | 批次 14:`unix_ls.py` 解析边界(空格文件名、异常行) | [ ] | 对照 `test_file_permissions.py` 补案例 | | POSIX-15 | 批次 15:前端 `remotePath.ts` 全页面扫尾 | [ ] | `rg` 无裸 `+` 路径拼接 | | POSIX-16 | 批次 16:`sync_engine_v2` rsync 目标再回归 | [ ] | `user@host:path` 形态 | @@ -229,11 +229,11 @@ flowchart LR ``` 代码实现 [x] POSIX 1-7 + 文件管理 P1-P5 + 二期 -本地单测门控 [ ] Phase A -正式审计 8 步 [ ] Phase D -生产部署 [ ] Phase B +本地单测门控 [x] Phase A(A-07 生产待重跑) +正式审计 8 步 [x] Phase D(合订 master-execution-audit) +生产部署 [~] Phase B(已 push+pull;restart+前端待完成) 浏览器 L1-L4 [ ] Phase C -POSIX 13-18 [ ] Phase E +POSIX 13-18 [~] Phase E(13 完成;14-18 待做) 全站 T1-T8 [ ] Phase F 增强 ENH [ ] Phase G(按需) ```