Initial commit: hardened DeerFlow factory
Vendored deer-flow upstream (bytedance/deer-flow) plus prompt-injection hardening: - New deerflow.security package: content_delimiter, html_cleaner, sanitizer (8 layers — invisible chars, control chars, symbols, NFC, PUA, tag chars, horizontal whitespace collapse with newline/tab preservation, length cap) - New deerflow.community.searx package: web_search, web_fetch, image_search backed by a private SearX instance, every external string sanitized and wrapped in <<<EXTERNAL_UNTRUSTED_CONTENT>>> delimiters - All native community web providers (ddg_search, tavily, exa, firecrawl, jina_ai, infoquest, image_search) replaced with hard-fail stubs that raise NativeWebToolDisabledError at import time, so a misconfigured tool.use path fails loud rather than silently falling back to unsanitized output - Native client back-doors (jina_client.py, infoquest_client.py) stubbed too - Native-tool tests quarantined under tests/_disabled_native/ (collect_ignore_glob via local conftest.py) - Sanitizer Layer 7 fix: only collapse horizontal whitespace, preserve newlines and tabs so list/table structure survives - Hardened runtime config.yaml references only the searx-backed tools - Factory overlay (backend/) kept in sync with deer-flow tree as a reference / source See HARDENING.md for the full audit trail and verification steps.
This commit is contained in:
156
deer-flow/backend/tests/test_credential_loader.py
Normal file
156
deer-flow/backend/tests/test_credential_loader.py
Normal file
@@ -0,0 +1,156 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from deerflow.models.credential_loader import (
|
||||
load_claude_code_credential,
|
||||
load_codex_cli_credential,
|
||||
)
|
||||
|
||||
|
||||
def _clear_claude_code_env(monkeypatch) -> None:
|
||||
for env_var in (
|
||||
"CLAUDE_CODE_OAUTH_TOKEN",
|
||||
"ANTHROPIC_AUTH_TOKEN",
|
||||
"CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR",
|
||||
"CLAUDE_CODE_CREDENTIALS_PATH",
|
||||
):
|
||||
monkeypatch.delenv(env_var, raising=False)
|
||||
|
||||
|
||||
def test_load_claude_code_credential_from_direct_env(monkeypatch):
|
||||
_clear_claude_code_env(monkeypatch)
|
||||
monkeypatch.setenv("CLAUDE_CODE_OAUTH_TOKEN", " sk-ant-oat01-env ")
|
||||
|
||||
cred = load_claude_code_credential()
|
||||
|
||||
assert cred is not None
|
||||
assert cred.access_token == "sk-ant-oat01-env"
|
||||
assert cred.refresh_token == ""
|
||||
assert cred.source == "claude-cli-env"
|
||||
|
||||
|
||||
def test_load_claude_code_credential_from_anthropic_auth_env(monkeypatch):
|
||||
_clear_claude_code_env(monkeypatch)
|
||||
monkeypatch.setenv("ANTHROPIC_AUTH_TOKEN", "sk-ant-oat01-anthropic-auth")
|
||||
|
||||
cred = load_claude_code_credential()
|
||||
|
||||
assert cred is not None
|
||||
assert cred.access_token == "sk-ant-oat01-anthropic-auth"
|
||||
assert cred.source == "claude-cli-env"
|
||||
|
||||
|
||||
def test_load_claude_code_credential_from_file_descriptor(monkeypatch):
|
||||
_clear_claude_code_env(monkeypatch)
|
||||
|
||||
read_fd, write_fd = os.pipe()
|
||||
try:
|
||||
os.write(write_fd, b"sk-ant-oat01-fd")
|
||||
os.close(write_fd)
|
||||
monkeypatch.setenv("CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR", str(read_fd))
|
||||
|
||||
cred = load_claude_code_credential()
|
||||
finally:
|
||||
os.close(read_fd)
|
||||
|
||||
assert cred is not None
|
||||
assert cred.access_token == "sk-ant-oat01-fd"
|
||||
assert cred.refresh_token == ""
|
||||
assert cred.source == "claude-cli-fd"
|
||||
|
||||
|
||||
def test_load_claude_code_credential_from_override_path(tmp_path, monkeypatch):
|
||||
_clear_claude_code_env(monkeypatch)
|
||||
cred_path = tmp_path / "claude-credentials.json"
|
||||
cred_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"claudeAiOauth": {
|
||||
"accessToken": "sk-ant-oat01-test",
|
||||
"refreshToken": "sk-ant-ort01-test",
|
||||
"expiresAt": 4_102_444_800_000,
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
monkeypatch.setenv("CLAUDE_CODE_CREDENTIALS_PATH", str(cred_path))
|
||||
|
||||
cred = load_claude_code_credential()
|
||||
|
||||
assert cred is not None
|
||||
assert cred.access_token == "sk-ant-oat01-test"
|
||||
assert cred.refresh_token == "sk-ant-ort01-test"
|
||||
assert cred.source == "claude-cli-file"
|
||||
|
||||
|
||||
def test_load_claude_code_credential_ignores_directory_path(tmp_path, monkeypatch):
|
||||
_clear_claude_code_env(monkeypatch)
|
||||
cred_dir = tmp_path / "claude-creds-dir"
|
||||
cred_dir.mkdir()
|
||||
monkeypatch.setenv("CLAUDE_CODE_CREDENTIALS_PATH", str(cred_dir))
|
||||
|
||||
assert load_claude_code_credential() is None
|
||||
|
||||
|
||||
def test_load_claude_code_credential_falls_back_to_default_file_when_override_is_invalid(tmp_path, monkeypatch):
|
||||
_clear_claude_code_env(monkeypatch)
|
||||
monkeypatch.setenv("HOME", str(tmp_path))
|
||||
|
||||
cred_dir = tmp_path / "claude-creds-dir"
|
||||
cred_dir.mkdir()
|
||||
monkeypatch.setenv("CLAUDE_CODE_CREDENTIALS_PATH", str(cred_dir))
|
||||
|
||||
default_path = tmp_path / ".claude" / ".credentials.json"
|
||||
default_path.parent.mkdir()
|
||||
default_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"claudeAiOauth": {
|
||||
"accessToken": "sk-ant-oat01-default",
|
||||
"refreshToken": "sk-ant-ort01-default",
|
||||
"expiresAt": 4_102_444_800_000,
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
cred = load_claude_code_credential()
|
||||
|
||||
assert cred is not None
|
||||
assert cred.access_token == "sk-ant-oat01-default"
|
||||
assert cred.refresh_token == "sk-ant-ort01-default"
|
||||
assert cred.source == "claude-cli-file"
|
||||
|
||||
|
||||
def test_load_codex_cli_credential_supports_nested_tokens_shape(tmp_path, monkeypatch):
|
||||
auth_path = tmp_path / "auth.json"
|
||||
auth_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"tokens": {
|
||||
"access_token": "codex-access-token",
|
||||
"account_id": "acct_123",
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
monkeypatch.setenv("CODEX_AUTH_PATH", str(auth_path))
|
||||
|
||||
cred = load_codex_cli_credential()
|
||||
|
||||
assert cred is not None
|
||||
assert cred.access_token == "codex-access-token"
|
||||
assert cred.account_id == "acct_123"
|
||||
assert cred.source == "codex-cli"
|
||||
|
||||
|
||||
def test_load_codex_cli_credential_supports_legacy_top_level_shape(tmp_path, monkeypatch):
|
||||
auth_path = tmp_path / "auth.json"
|
||||
auth_path.write_text(json.dumps({"access_token": "legacy-access-token"}))
|
||||
monkeypatch.setenv("CODEX_AUTH_PATH", str(auth_path))
|
||||
|
||||
cred = load_codex_cli_credential()
|
||||
|
||||
assert cred is not None
|
||||
assert cred.access_token == "legacy-access-token"
|
||||
assert cred.account_id == ""
|
||||
Reference in New Issue
Block a user