補市場情報樣本審核 CSRF
All checks were successful
CD Pipeline / deploy (push) Successful in 1m2s

This commit is contained in:
OoO
2026-05-19 00:45:14 +08:00
parent f221832735
commit 83fd05d614
4 changed files with 8 additions and 2 deletions

View File

@@ -106,6 +106,7 @@
- Phase 48 manual sample acceptance contract新增 `/api/market_intel/manual_sample_acceptance` 與 UI「樣本結果驗收契約」panel定義 sample result 必要欄位、diagnostics 欄位、驗收門檻、拒收條件、人工決策與升級順序,不載入 sample result、不抓外站、不允許候選導入、不寫 DB版本同步至 V10.215。
- Phase 49 manual sample result review新增 `services/market_intel/manual_sample_review.py`、`/api/market_intel/manual_sample_review` 與 UI「樣本結果審核預覽」panel以純函式評估人工 sample result 是否可進候選預覽,預設不載入結果、不抓外站、不存檔、不寫 DB、不允許候選導入、不掛 scheduler版本同步至 V10.216。
- Phase 50 manual sample review evaluate新增 `/api/market_intel/manual_sample_review/evaluate` POST 與 UI JSON 審核入口,允許操作員貼入單筆 sample result 即時回傳 PASS/BLOCK不保存 payload、不回吐完整 HTML、不寫 DB、不建立候選活動、不允許候選導入、不掛 scheduler版本同步至 V10.219。
- V10.220 補 Phase 50 UI POST CSRF header`manual_sample_review/evaluate` 保持 CSRF 保護,頁面 fetch 送出 `X-CSRFToken`,不豁免安全檢查。
- Schema smoke`tests/test_market_intel_skeleton.py` 檢查 `Base.metadata` 內含 ADR-035 八張 `market_*` tables。
- Desktop UI QA本機只註冊 `market_intel_bp` 的 Flask harness 載入 `/market_intel`,確認 Phase 15、候選預覽、writer preview、安全 flags、點陣暖紙視覺正常console error 0。
- API QA`/api/market_intel/schema_smoke` 通過 7 張表與 `market_platforms` 必要欄位檢查;`/api/market_intel/platform_seed_writer_plan` 回傳 4 筆 dry-run upsert preview`writes_executed=false`,四平台皆 `blocked_dry_run_only`。

View File

@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.219"
SYSTEM_VERSION = "V10.220"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -849,6 +849,7 @@
const meta = root ? root.querySelector('[data-market-intel-preview-meta]') : null;
const body = root ? root.querySelector('[data-market-intel-preview-body]') : null;
const refresh = root ? root.querySelector('[data-market-intel-refresh]') : null;
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
const endpoint = "{{ url_for('market_intel.market_intel_candidate_preview') }}?fetch=false&limit=20";
const writerMeta = writerRoot ? writerRoot.querySelector('[data-market-intel-writer-meta]') : null;
const writerBody = writerRoot ? writerRoot.querySelector('[data-market-intel-writer-body]') : null;
@@ -1983,7 +1984,10 @@
const response = await fetch(sampleReviewEvaluateEndpoint, {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
body: JSON.stringify({ sample_result: parsed })
});
const data = await response.json();

View File

@@ -537,6 +537,7 @@ def test_market_intel_preview_template_uses_safe_fetch_false_endpoint():
assert "market_intel.market_intel_manual_sample_acceptance" in template
assert "market_intel.market_intel_manual_sample_review" in template
assert "market_intel.market_intel_manual_sample_review_evaluate" in template
assert "X-CSRFToken" in template
assert "market_intel.market_intel_scheduler_plan" in template
assert "market_intel.market_intel_match_review_plan" in template
assert "market_intel.market_intel_opportunity_plan" in template