fix: API route security fixes (path traversal, auth, bare except)

- articles.py: Path traversal sanitization
- optimizer_logs.py: Admin auth guard
- platform_config.py: Admin auth guard
- system.py: Path traversal whitelist
- topic_config.py: Admin auth guard
- topics.py: Minor fix

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Yuzhiran Dev
2026-06-16 08:23:50 +08:00
parent 8595bbc521
commit d601a26850
6 changed files with 33 additions and 18 deletions
+3
View File
@@ -1,3 +1,4 @@
import re
from fastapi import APIRouter, HTTPException, Query, Depends, Body
from sqlalchemy.orm import Session
from sqlalchemy import or_
@@ -171,6 +172,8 @@ def get_optimization_report(publish_date: str = None, current_user: User = Depen
"""获取合规优化报告"""
if not publish_date:
publish_date = date.today().isoformat()
if not re.match(r'^\d{4}-\d{2}-\d{2}$', publish_date):
raise HTTPException(status_code=400, detail="Invalid date format (expected YYYY-MM-DD)")
PROJECT_ROOT = Path('/root/openclaw-workspace/projects/yu-zhi-ran')
report_path = PROJECT_ROOT / "automation" / "data" / "drafts" / publish_date / "optimization_report.json"
if not report_path.exists():
@@ -1,3 +1,4 @@
import re
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session
from pathlib import Path
@@ -58,6 +59,11 @@ def get_logs(
"""
if not type or not date:
raise HTTPException(status_code=400, detail="type and date parameters are required")
_ALLOWED_LOG_TYPES = {"creator", "collector", "optimizer", "sources", "metrics", "trends", "rank_tracker"}
if type not in _ALLOWED_LOG_TYPES:
raise HTTPException(status_code=400, detail=f"Invalid log type: {type}")
if not re.match(r'^\d{4}-\d{2}-\d{2}$', date):
raise HTTPException(status_code=400, detail="Invalid date format (expected YYYY-MM-DD)")
logs_dir = PROJECT_ROOT / "automation" / "logs"
filename = f"{type}_{date}.log"
log_path = logs_dir / filename
+6 -6
View File
@@ -5,7 +5,7 @@ from typing import List, Optional
from ..database import get_db
from ..models import PlatformConfig
from ..schemas import PlatformConfigCreate, PlatformConfigUpdate, PlatformConfigResponse
from .auth import get_current_user
from .auth import get_current_admin
router = APIRouter(prefix="/api/platform-config", tags=["platform-config"])
@@ -14,7 +14,7 @@ router = APIRouter(prefix="/api/platform-config", tags=["platform-config"])
def list_platforms(
active_only: bool = True,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
query = db.query(PlatformConfig)
if active_only:
@@ -26,7 +26,7 @@ def list_platforms(
def create_platform(
data: PlatformConfigCreate,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
existing = db.query(PlatformConfig).filter(PlatformConfig.platform == data.platform).first()
if existing:
@@ -39,7 +39,7 @@ def create_platform(
@router.get("/{platform}", response_model=PlatformConfigResponse)
def get_platform(platform: str, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
def get_platform(platform: str, db: Session = Depends(get_db), current_user=Depends(get_current_admin)):
p = db.query(PlatformConfig).filter(PlatformConfig.platform == platform).first()
if not p:
raise HTTPException(status_code=404, detail="平台不存在")
@@ -47,7 +47,7 @@ def get_platform(platform: str, db: Session = Depends(get_db), current_user=Depe
@router.put("/{platform}", response_model=PlatformConfigResponse)
def update_platform(platform: str, data: PlatformConfigUpdate, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
def update_platform(platform: str, data: PlatformConfigUpdate, db: Session = Depends(get_db), current_user=Depends(get_current_admin)):
p = db.query(PlatformConfig).filter(PlatformConfig.platform == platform).first()
if not p:
raise HTTPException(status_code=404, detail="平台不存在")
@@ -59,7 +59,7 @@ def update_platform(platform: str, data: PlatformConfigUpdate, db: Session = Dep
@router.delete("/{platform}")
def delete_platform(platform: str, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
def delete_platform(platform: str, db: Session = Depends(get_db), current_user=Depends(get_current_admin)):
p = db.query(PlatformConfig).filter(PlatformConfig.platform == platform).first()
if not p:
raise HTTPException(status_code=404, detail="平台不存在")
+7 -1
View File
@@ -17,7 +17,7 @@ from ..core.collector import run_collector, get_collector_status, _running_proce
import threading
from ..core.sync import sync_all_topics
from ..core.scheduler import scheduler
from .auth import get_current_user, org_filter
from .auth import get_current_user, get_current_admin, org_filter
PROJECT_ROOT = Path(__file__).resolve().parents[4]
if os.getenv('PROJECT_ROOT'):
@@ -211,8 +211,14 @@ def review_status():
return {"status": "idle", "message": "当前无运行中的审查任务"}
return status
_ALLOWED_LOG_TYPES = {"creator", "collector", "optimizer", "sources", "metrics", "trends", "rank_tracker"}
@router.get("/logs/{log_date}", dependencies=[Depends(get_current_user)])
def get_logs(log_date: str, log_type: str = "creator"):
if log_type not in _ALLOWED_LOG_TYPES:
raise HTTPException(status_code=400, detail=f"Invalid log_type: {log_type}")
if not re.match(r'^\d{4}-\d{2}-\d{2}$', log_date):
raise HTTPException(status_code=400, detail="Invalid log_date format (expected YYYY-MM-DD)")
log_file = LOGS_DIR / f"{log_type}_{log_date}.log"
if not log_file.exists():
raise HTTPException(status_code=404, detail=f"Log file not found: {log_file}")
+10 -10
View File
@@ -9,7 +9,7 @@ from ..schemas import (
TopicFieldBase, TopicFieldResponse,
TopicConfigFieldBase, TopicConfigFieldResponse
)
from .auth import get_current_user
from .auth import get_current_admin
router = APIRouter(prefix="/api/topic-config", tags=["topic-config"])
@@ -18,7 +18,7 @@ router = APIRouter(prefix="/api/topic-config", tags=["topic-config"])
def list_fields(
include_inactive: bool = False,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
query = db.query(TopicField)
if not include_inactive:
@@ -30,7 +30,7 @@ def list_fields(
def create_field(
data: TopicFieldBase,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
existing = db.query(TopicField).filter(TopicField.name == data.name).first()
if existing:
@@ -47,7 +47,7 @@ def update_field(
field_id: int,
data: TopicFieldBase,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
field = db.query(TopicField).filter(TopicField.id == field_id).first()
if not field:
@@ -63,7 +63,7 @@ def update_field(
def delete_field(
field_id: int,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
field = db.query(TopicField).filter(TopicField.id == field_id).first()
if not field:
@@ -77,7 +77,7 @@ def delete_field(
def get_scoring_fields(
field_id: int,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
field = db.query(TopicField).filter(TopicField.id == field_id).first()
if not field:
@@ -92,7 +92,7 @@ def create_scoring_field(
field_id: int,
data: TopicConfigFieldBase,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
field = db.query(TopicField).filter(TopicField.id == field_id).first()
if not field:
@@ -117,7 +117,7 @@ def update_scoring_field(
config_id: int,
data: TopicConfigFieldBase,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
cfg = db.query(TopicConfigField).filter(TopicConfigField.id == config_id).first()
if not cfg:
@@ -133,7 +133,7 @@ def update_scoring_field(
def delete_scoring_field(
config_id: int,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
cfg = db.query(TopicConfigField).filter(TopicConfigField.id == config_id).first()
if not cfg:
@@ -148,7 +148,7 @@ def batch_create_scoring_fields(
field_id: int,
fields: List[TopicConfigFieldBase],
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(get_current_admin)
):
field = db.query(TopicField).filter(TopicField.id == field_id).first()
if not field:
+1 -1
View File
@@ -167,7 +167,7 @@ def create_topic(
try:
num = int(max_topic.id[1:]) + 1
topic_id = f"T{num:03d}"
except:
except (ValueError, TypeError):
topic_id = f"T{datetime.now().strftime('%m%d%H%M')}"
else:
topic_id = f"T{datetime.now().strftime('%m%d%H%M')}"