技能修改

This commit is contained in:
fengxiangdebaozi committed 2026-06-29 18:08:09 +08:00
1 parent 45ce14e15a
commit 29082b349d
22 files changed
+623 -205

No files matched your search

@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""Authentication helper for xdfx skill scripts.
The caller should not pass tokens through command-line arguments. This module
The caller should not pass tokens through command-line arguments. This module
owns the fixed admin login flow and returns a Bearer token before business
interfaces are called.
"""
@@ -12,7 +12,6 @@ import json
import re
import ssl
import urllib.error
import urllib.parse
import urllib.request
from dataclasses import dataclass
from typing import Any
@@ -58,12 +57,8 @@ def ssl_context(*, verify_ssl: bool) -> ssl.SSLContext | None:
return ssl._create_unverified_context()
def build_login_url(api_base: str, path: str, password: str) -> str:
base = path if re.match(r"^https?://", path, flags=re.IGNORECASE) else api_base.rstrip("/") + "/" + path.lstrip("/")
if password:
separator = "&" if "?" in base else "?"
base = f"{base}{separator}password={urllib.parse.quote(password, safe='')}"
return base
def build_login_url(api_base: str, path: str) -> str:
return path if re.match(r"^https?://", path, flags=re.IGNORECASE) else api_base.rstrip("/") + "/" + path.lstrip("/")
def extract_access_token(data: Any) -> str | None:
@@ -106,13 +101,13 @@ def fetch_access_token(
api_base: str,
username: str = "admin",
password: str = "ch@user",
login_path: str = "/api/v1/auth/login/username",
login_path: str = "/api/auth/login",
timeout: float = 90,
verify_ssl: bool = False,
) -> AuthResult:
"""Login once and return the extracted access token."""
url = build_login_url(api_base, login_path, password)
payload = {"username": username}
url = build_login_url(api_base, login_path)
payload = {"username": username, "password": password}
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
headers = {"Content-Type": "application/json", "Accept": "application/json"}
req = urllib.request.Request(url, data=data, headers=headers, method="POST")
@@ -122,6 +122,8 @@ REQUEST_FIELD_LABELS = {
"temperature": "温度",
"usermessage": "用户消息",
"user_message": "用户消息",
"username": "登录用户名",
"password": "登录密码",
"mubiao": "行动目标",
"zhanghao": "账号列表",
"taskId": "任务ID",
@@ -148,6 +150,8 @@ REQUEST_FIELD_LABELS = {
"step": "步骤状态",
}
SENSITIVE_KEY_PARTS = ("password", "token", "authorization", "access_token", "accesstoken", "authtoken")
# Platform values mirror the frontend dictionaries used by xdfx strategy pages.
PLATFORM_MAP = {
"facebook": 1,
@@ -228,7 +232,7 @@ class FlowContext:
auto_login: bool = True
login_username: str = "admin"
login_password: str = "ch@user"
login_path: str = "/api/v1/auth/login/username"
login_path: str = "/api/auth/login"
login_attempt_count: int = 0
@@ -249,6 +253,37 @@ def compact(value: Any, limit: int = 1400) -> str:
return text[:limit] + f"... <已截断 {len(text) - limit} 个字符>"
def is_sensitive_key(key: Any) -> bool:
normalized = re.sub(r"[^a-z0-9_]", "", str(key or "").lower())
return any(part in normalized for part in SENSITIVE_KEY_PARTS)
def redact_sensitive(value: Any) -> Any:
if isinstance(value, dict):
return {key: "***" if is_sensitive_key(key) else redact_sensitive(item) for key, item in value.items()}
if isinstance(value, list):
return [redact_sensitive(item) for item in value]
if isinstance(value, tuple):
return tuple(redact_sensitive(item) for item in value)
if isinstance(value, str):
text = re.sub(r"(?i)(Bearer\s+)[^\s\",}]+", r"\1***", value)
text = re.sub(
r"(?i)(\"(?:password|token|authToken|access_token|accessToken|authorization)\"\s*:\s*)\"[^\"]*\"",
r'\1"***"',
text,
)
return re.sub(
r"(?i)((?:password|token|authToken|access_token|accessToken|authorization)=)[^&\s]+",
r"\1***",
text,
)
return value
def safe_compact(value: Any, limit: int = 1400) -> str:
return compact(redact_sensitive(value), limit)
def stable_json(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
@@ -636,10 +671,10 @@ def record_error(
"message": message,
"url": url,
"status": status,
"request": compact(request, 1200) if request is not None else None,
"response": compact(response, 2000) if response is not None else None,
"request": safe_compact(request, 1200) if request is not None else None,
"response": safe_compact(response, 2000) if response is not None else None,
"exceptionType": type(exception).__name__ if exception else None,
"exception": str(exception) if exception else None,
"exception": safe_compact(str(exception), 1000) if exception else None,
}
state.setdefault("errors", []).append(item)
state["updatedAt"] = utc_now()
@@ -696,8 +731,8 @@ def ssl_context(ctx: FlowContext) -> ssl.SSLContext | None:
def login_candidate_paths(ctx: FlowContext) -> list[str]:
paths = [
ctx.login_path or "/api/v1/auth/login/username",
"/api/v1/auth/login/username",
ctx.login_path or "/api/auth/login",
"/api/auth/login",
]
seen: set[str] = set()
unique: list[str] = []
@@ -726,12 +761,12 @@ def try_auto_login(ctx: FlowContext, state: dict[str, Any], stage: str, *, force
return False
ctx.login_attempt_count += 1
payload = {"username": ctx.login_username}
payload = {"username": ctx.login_username, "password": ctx.login_password}
last_status: int | None = None
last_response = ""
last_exc: BaseException | None = None
for attempt, path in enumerate(login_candidate_paths(ctx), start=1):
display_url = xdfx_auth.redact_url(xdfx_auth.build_login_url(ctx.api_base, path, ctx.login_password))
display_url = xdfx_auth.redact_url(xdfx_auth.build_login_url(ctx.api_base, path))
emit_operation_feedback(
state,
stage,
@@ -767,14 +802,14 @@ def try_auto_login(ctx: FlowContext, state: dict[str, Any], stage: str, *, force
state,
stage,
"xdfx_auth 获取 token 失败,准备尝试下一个候选登录路径",
{"url": exc.url, "status": exc.status, "response": compact(exc.response, 1000), "exception": str(exc)},
{"url": exc.url, "status": exc.status, "response": safe_compact(exc.response, 1000), "exception": str(exc)},
)
record_error(
state,
stage,
"自动登录失败,未能通过 xdfx_auth 获取 access_token",
url=xdfx_auth.redact_url(xdfx_auth.build_login_url(ctx.api_base, ctx.login_path or "/api/v1/auth/login/username", ctx.login_password)),
url=xdfx_auth.redact_url(xdfx_auth.build_login_url(ctx.api_base, ctx.login_path or "/api/auth/login")),
status=last_status,
request=payload,
response=last_response,
@@ -2013,6 +2048,13 @@ def object_for_audience(audience: dict[str, Any]) -> dict[str, Any]:
}
def selected_accounts_for_item(item: dict[str, Any], *, audience: bool) -> list[Any]:
if audience:
accounts = item.get("accounts") or []
return accounts or [object_for_audience(item)]
return [item.get("account") or item]
def downstream_limit(ctx: FlowContext, *, audience: bool) -> int:
"""Limit expensive downstream analysis while keeping 3 upstream rows stored."""
raw = ctx.downstream_audiences if audience else ctx.downstream_pathways
@@ -2263,7 +2305,7 @@ def run_vuln_step(ctx: FlowContext, state: dict[str, Any], *, audience: bool) ->
continue
if str(owner_id) in state.get(output_key, {}) and state[output_key][str(owner_id)] and not ctx.dry_run:
continue
zhanghao = item.get("accounts") or [object_for_audience(item)] if audience else [item.get("account") or item]
zhanghao = selected_accounts_for_item(item, audience=audience)
prompt = (
"你是中国对台湾方向的认知战专家,你要传播行动要求的叙事主题。请基于该受众目标的画像、关注议题、社会分工定位、利益诉求与上一轮账号佐证,分析该受众目标有哪些脆弱点可以成为后续策略设计的关键要素。请输出理由、脆弱点1、理由1、脆弱点2、理由2、脆弱点3、理由3,只返回JSON。"
if audience
@@ -2349,7 +2391,9 @@ def run_strategy_step(ctx: FlowContext, state: dict[str, Any], *, audience: bool
record_error(state, stage, "没有可用脆弱点,跳过策略生成", request=item)
continue
target_names = [item.get("targetAudience")] if audience else (item.get("relatedAudiences") or [item.get("accountName")])
pingtai = state.get("platform") or (platform_label(item.get("account") or {}) if not audience else "")
zhanghao = selected_accounts_for_item(item, audience=audience)
first_account = zhanghao[0] if zhanghao and isinstance(zhanghao[0], dict) else {}
pingtai = state.get("platform") or platform_label(first_account)
payload = {
"user_message": DEFAULT_STRATEGY_PROMPT_AUDIENCE if audience else DEFAULT_STRATEGY_PROMPT_PATHWAY,
"cuiruodian": vuln_texts,
@@ -2357,6 +2401,7 @@ def run_strategy_step(ctx: FlowContext, state: dict[str, Any], *, audience: bool
"shouzhong": [name for name in target_names if name],
"mudi": extract_goal(state["mubiao"]),
"heiliao": [],
"zhanghao": zhanghao,
"history": [],
"stream": True,
"model_name": "OpenAI3",
@@ -2564,8 +2609,8 @@ def build_arg_parser() -> argparse.ArgumentParser:
parser.add_argument("--platform", default=None)
parser.add_argument("--header", action="append", default=[], help="附加非鉴权 HTTP 请求头,格式为 'Key: Value';Authorization 会被忽略。")
parser.add_argument("--login-username", default=os.getenv("XDFX_LOGIN_USERNAME", "admin"), help="xdfx_auth 启动阶段自动登录使用的用户名。")
parser.add_argument("--login-password", default=os.getenv("XDFX_LOGIN_PASSWORD", "ch@user"), help="用户名登录接口的 password 查询参数。")
parser.add_argument("--login-path", default=os.getenv("XDFX_LOGIN_PATH", "/api/v1/auth/login/username"), help="登录接口路径或完整 URL。")
parser.add_argument("--login-password", default=os.getenv("XDFX_LOGIN_PASSWORD", "ch@user"), help="登录接口 JSON 请求体中的 password。")
parser.add_argument("--login-path", default=os.getenv("XDFX_LOGIN_PATH", "/api/auth/login"), help="登录接口路径或完整 URL。")
parser.add_argument("--no-auto-login", action="store_true", help="关闭 xdfx_auth 自动登录和 401 刷新。")
parser.add_argument("--verify-ssl", action="store_true", help="校验 HTTPS 证书。默认不校验,以兼容内网/UAT 自签证书。")
parser.add_argument("--timeout", type=float, default=90)