Skip to content

Add searxng-search: web search via self-hosted SearXNG instance - #9

Closed
Fectivnfy112357 wants to merge 4 commits into
hetaoBackend:mainfrom
Fectivnfy112357:feat/searxng-search
Closed

Add searxng-search: web search via self-hosted SearXNG instance#9
Fectivnfy112357 wants to merge 4 commits into
hetaoBackend:mainfrom
Fectivnfy112357:feat/searxng-search

Conversation

@Fectivnfy112357

Copy link
Copy Markdown
Collaborator

What it solves

Web search inside an agent usually means a vendor API with credentials and
quotas. If you already run SearXNG (a privacy-respecting metasearch engine),
this skill gives the agent a first-class search tool against it — one config
file, no vendor SDK, no tracking.

Try it

Install from /pluginsLocal, configure your instance, then ask:

search the web for the latest SearXNG documentation

Expected result: a formatted list of results (title, URL, snippet) from
your SearXNG instance, filtered by the requested category / time range /
language.

Direct usage:

python3 scripts/search.py -c news -t day "latest tech news"
python3 scripts/search.py -e google,duckduckgo -p 2 "rust programming"
python3 scripts/search.py -l zh-CN -n 10 "开源搜索引擎"

Dependencies and platforms

  • A self-hosted SearXNG instance reachable over HTTP(S) — the user
    provides it; no public instance is bundled.
  • Python 3.11+ (TOML config; legacy JSON config works on older Python).
  • A config file at ~/.config/agents/searxng.toml (or $XDG_CONFIG_HOME);
    all fields documented in skills/searxng-search/references/configuration.md.

Network and data

  • The script talks only to the user's configured SearXNG instance (the
    base_url in the local config). No other network destinations.
  • Credentials (bearer token / basic auth, if any) live in the user's local
    config file; never sent anywhere else.
  • No telemetry, no third-party services.

Test evidence

  • npm run validateOK plugin Fectivnfy112357/searxng-search
  • Script exercised daily against the contributor's own SearXNG instance
    (categories, time range, language and pagination paths in active use).

Agent-plugin contribution for the community registry:
plugins/Fectivnfy112357/searxng-search/

@hetaoBackend hetaoBackend left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

暂不符合收录门禁:

  1. base_url 接受任意 http://,同时脚本可能发送 Bearer/Basic Authorization;这会把凭据明文暴露在链路上。请默认强制 HTTPS,仅允许明确的 loopback HTTP(或要求用户显式 unsafe opt-in 并强警告),并补测试。
  2. “No third-party services / only configured SearXNG instance” 的披露不完整:客户端直连实例没错,但搜索 query 会由该实例继续发往配置的 engines/providers。请区分 direct destination 与 downstream destinations,说明 query、语言、分类等数据可能被哪些搜索引擎处理。
  3. 配置示例鼓励把 token/password 明文写入文件,但没有文件权限提示。请优先环境变量引用,并检查/警告过宽权限(适用平台上)。
  4. 请补可复现测试,覆盖 URL 校验、HTTP/HTTPS 规则、auth header、超时、非 JSON/错误响应和不在错误信息中泄露凭据。

修复后我会按新 head 复核。

…ure, env-var, tests

针对 #9 审阅的 4 条 credential / 披露 / 凭据 / 测试门禁要求做修复。

## 1. HTTPS 强制 + URL 校验
- 新增 `validate_base_url(url, config)`:
  HTTPS 一律放行;HTTP 仅允许 loopback 主机(127.0.0.1 / ::1 / localhost / 0.0.0.0);
  非 loopback HTTP 需 config 中显式 `allow_insecure_http = true` 并发出强警告
  (含凭据明文链路风险),否则 die。
- 在 `load_config()` 末尾调用 `validate_base_url`。
- 不再接受 ftp / file 等非 http(s) scheme。

## 2. direct vs downstream 披露
- `README.md` "Data and network" 拆为 "Direct destination (the script itself)"
  与 "Downstream destinations (the SearXNG instance, not the script)",列出
  引擎名(Google / Bing / DuckDuckGo / Brave / Baidu 等由实例运维者配置)。
- `SKILL.md` 顶部新增 "Network destinations" 节,同一区分。
- `plugin.json` description 同步更新。

## 3. env-var 优先 + 文件权限
- `references/configuration.md` 顶部加 "Recommended: keep secrets out of
  the file",env-var 示例前置;明文示例移到 "Plaintext secrets in the
  config" 节,并配 `chmod 600` 警告。
- 新增 `check_file_permissions(path)`:POSIX 上 `mode & 0o077` 时 warn;
  Windows / 缺文件不告警。
- `load_config()` 末尾调用 `check_file_permissions`。

## 4. 可复现回归测试
- `scripts/tests/test_url_validation.py` (9): HTTPS / loopback / opt-in
  / non-loopback reject / 非法 scheme / 缺 host。
- `scripts/tests/test_auth_header.py` (8): bearer / basic 头构造,env-var
  解析,User-Agent 引用真实仓库,token 不漏到 URL。
- `scripts/tests/test_redaction.py` (5): 5 种凭据形态 + die/warn 路径
  不回显凭据。
- `scripts/tests/test_config.py` (11): 缺文件 / 坏 TOML / 缺 base_url /
  env-var 未设 / 文件权限告警。
- `scripts/tests/test_timeout.py` (2): 默认 30s + 配置覆盖。
- `scripts/tests/test_invalid_response.py` (5): 非 JSON / 结构化 error
  / HTTPError body 含凭据被 mask / URLError / TimeoutError。
- `scripts/run_tests.py` (unittest discover 入口) +
  `test/searxng-search.test.mjs` (node --test bridge)。

## 顺带修
- User-Agent: `XYenon/agents` -> `Fectivnfy112357/MiniMax-Code-Plugins`
- 重构 `print(..., file=sys.stderr); sys.exit(1)` 为 `die(msg)` / `warn(msg)`
  助手(消除 10+ 处重复)。
- 新增 `__pycache__/` / `*.pyc` 到 plugin-local `.gitignore`。

测试结果: 40/40 OK; node --test pass 1 fail 0; validator OK
The previous implementation always ran `os.stat().st_mode & 0o077`,
but on Windows `st_mode` is a synthetic value (typically 0o666) and
is not derived from the file's real ACL. The docstring already
promised to skip the check on non-POSIX; the implementation now
matches by guarding with `os.name != "posix"`.

The two POSIX-only tests now self-skip on Windows, and a new
`test_skipped_on_non_posix` locks in the no-op behavior. Total
test count goes from 40 to 41 (2 skipped on Windows).

Found by real test against `http://textvision.top:40001/` (a
SearXNG 2026.8.3 instance) where the spurious permission
warning fired on every run despite the fix path not being
available on Windows.
@Fectivnfy112357

Fectivnfy112357 commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

@hetaoBackend 谢谢这次的反馈,4 条都很到位 — credential 链路、direct vs downstream 披露、env-var 优先、可复现测试,确实都是我之前没充分考虑的部分。在 commit a49ad61 + b31a104 里逐条修了,麻烦再审一下。

1. HTTPS 强制 + URL 校验:新增 validate_base_url(url, config),HTTPS 一律放行;HTTP 仅允许 loopback(127.0.0.1 / ::1 / localhost / 0.0.0.0);非 loopback HTTP 需 allow_insecure_http = true opt-in + 强警告("凭据明文链路"),否则 die。非 http(s) scheme / 缺 host 拒。test_url_validation.py 9 个 case 全覆盖。

2. direct vs downstream 披露SKILL.md / README.md / plugin.json 三处都明确"两层"——脚本只连你的 SearXNG 实例(HTTPS 优先 / 凭据仅到这一台),实例再把 query / language / category 转发给它自己配置的 Google / Bing / DuckDuckGo / Brave / Baidu 等引擎(由实例运维者配置,不由本 skill 控制)。

3. env-var 优先 + 文件权限configuration.md 顶部加 "Recommended: keep secrets out of the file";env-var 示例前置,明文示例移到"Plaintext secrets"节并配 chmod 600 警告。新增 check_file_permissions(POSIX-only,Windows 自动 skip)+ 新增故障排查条目。

4. 可复现测试:6 套 stdlib unittest,共 41 个 case(Windows 上 2 个 POSIX-only skip),run_tests.py 入口 + test/searxng-search.test.mjs 接入 node --test。覆盖 URL/HTTPS、bearer/basic auth header、env-var 解析、限流/超时、非 JSON / 结构化 error / HTTPError body 凭据 mask、文件权限。

实测python run_tests.pyRan 41 tests in 0.025s — OK (skipped=2)node --test pass 1 fail 0;validate.mjsOK plugin Fectivnfy112357/searxng-search。真查询 http://testsearxng:40001/ 跑通(这是我自己托管的 SearXNG 实例,按 opt-in 路径走 HTTP),结果里 engines: 字段实证了 Req 2 的下游链路。

顺带:实测时发现 Windows 上 os.stat().st_mode 是合成值,原 check_file_permissions 在 Windows 误报,改成 if os.name != "posix": return,并在 commit b31a104 里加了 test_skipped_on_non_posix

diff 在 feat/searxng-search 顶端 b31a10427 files / +1042 / -96(含 a49ad61)。辛苦再审一次。

@hetaoBackend hetaoBackend left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fectivnfy112357 最新 head b31a104 仍有一个已复现的凭据泄露问题:

  1. search.py 给请求添加 Authorization 后使用默认 urllib redirect handler。测试中,初始 loopback URL 返回跨端口 302 时,Bearer token 被原样转发到第二个 origin,也可绕过初始 HTTPS/loopback 校验。请禁用 redirect,或逐跳限制同 origin、禁止 HTTPS→HTTP,并补回归测试。
  2. 根测试固定执行 python,在只有 python3 的受支持环境中 npm run check26/27;请做跨平台解释器探测。
  3. 当前形状脱敏不能覆盖实际短 token,服务端 body/error 可能回显配置 secret。请对本次实际 secret 做精确替换,认证错误不要直接回显 body。

修复以上 P1 并跑出 CI/CodeQL 后再复审。

@Fectivnfy112357
Fectivnfy112357 marked this pull request as draft August 15, 2026 16:33
…ion, portable tests

Round-2 P1 fixes from reviewer hetaoBackend:

- Block all HTTP redirects (_SafeRedirectHandler, replaces urllib's
  default handler): a 30x response is an error with a safe diagnostic,
  so the Authorization header can never be forwarded to a different
  origin or downgraded HTTPS->HTTP. Regression tests + live loopback
  cross-port 302 check.
- Exact-secret redaction: register_secret()/reset_active_secrets() track
  the actual token/password/base64 used for the request; redact_secrets
  substitutes them by value before shape-based fallback, catching short
  or non-shape tokens. 401/403/407 response bodies are suppressed
  entirely so a server that reflects credentials cannot leak them.
- Cross-platform interpreter discovery (py -3 / python / python3, each
  verified via --version) with an explicit failure when none is found,
  instead of a misleading 26/27 pass. The node bridge now lives inside
  the Plugin (plugins/<owner>/<plugin>/test/) so the contribution stays
  self-contained; the repo-root bridge was removed.

Additional hardening found in self-review:

- Cap response/error bodies at 10 MiB (MAX_RESPONSE_BYTES).
- Validate numeric config fields (timeout, default_max_results) so a
  string value exits cleanly instead of raising a raw TypeError.
- Require a JSON object root from the instance; reject non-UTF-8 bodies
  with a clean diagnostic instead of a traceback.
- Custom config headers now resolve $ENV_VAR references and register
  resolved values for exact redaction, matching auth.* handling.
- Remove unused info() helper; add close() to the HTTPError fixture to
  silence urllib addbase GC cleanup noise.

Docs: SKILL.md/README disclose that redirects are never followed;
configuration.md documents $ENV_VAR in headers and a redirect-blocked
troubleshooting entry.

Test suite: 77 stdlib unittest cases (2 POSIX-only skips), wired into
npm test via the in-plugin node bridge; validate.mjs passes.
@Fectivnfy112357

Copy link
Copy Markdown
Collaborator Author

@hetaoBackend 谢谢这次反馈,3 条 P1 都收到,已在 commit c80de45 逐条修完,并顺手做了一轮额外的健壮性加固。CI 与 CodeQL 均已通过(validate / analyze / CodeQL 全绿)。

1. redirect 凭据泄露search.py 新增 _SafeRedirectHandler,替换 urllib 默认 redirect handler,所有 30x 一律拦截并报错(不再逐跳判断同源/同端口/同 scheme——直接拒绝,让用户把 base_url 指向最终端点或用同源反代)。诊断信息只显示 scheme://host[:port]/path,剥离 query/fragment,避免 Location 里嵌 token 回显。实测复现了你描述的场景:loopback 初始 URL 返回跨端口 302,第二个 origin 收到的 AuthorizationNone(Bearer 未被转发)。test_redirect.py 12 个 case 覆盖全部 30x 状态码、诊断形状(剥离 query/fragment)、opener 无默认 handler 残留、main() 集成路径。

2. 跨平台解释器探测:桥接改为按平台探测——Windows 优先 py -3pypythonpython3,POSIX 优先 python3python,每个候选先用 --version 验证是真 Python(防 Windows 命令名撞车/py 是 pip shim),全找不到则显式报错,不再出现 26/27 假通过。另外按社区规范把桥接测试从仓库根 test/ 移入了插件目录(plugins/Fectivnfy112357/searxng-search/test/),node --test 从仓库根递归发现,CI 照常运行,插件保持自包含。

3. 精确脱敏 + 认证错误不回显 bodyregister_secret() / reset_active_secrets() 记录本次请求实际使用的 token / user / pass / base64 凭据,redact_secrets 先按值精确替换(最长优先)再形状兜底——短 token、非形状 token 也能遮掉,且每次 load_config 清空注册表防跨运行残留。401/403/407 响应体整体抑制不回显(服务端回显凭据的残余面封死),500 仍回显但走脱敏。test_redaction.py 新增 8 个精确注册表 case,test_invalid_response.py 新增 4 个认证 body 抑制 case(含 500 必须回显的反例)。

顺带加固(自查发现,不在您原话内):

  • 响应/错误体读取加 10 MiB 上限MAX_RESPONSE_BYTES),超限干净报错;
  • timeout / default_max_results 配置类型校验(字符串/布尔直接清晰报错,不再裸 TypeError traceback);
  • JSON 根必须为对象、非 UTF-8 响应显式报错(不再 AttributeError / UnicodeDecodeError traceback);
  • 自定义 headers 值支持 $ENV_VAR 解析并纳入精确脱敏,与 auth.* 一致;
  • SKILL.md / README 披露"不跟随 redirect";configuration.md 补 headers 环境变量说明 + redirect blocked 排障条目。

测试结果python run_tests.pyRan 77 tests in ~0.1s — OK (skipped=2)(相比上轮 41 个,新增 36 个);node --test pass 1 fail 0;validate.mjsOK plugin Fectivnfy112357/searxng-search。CI(validate)与 CodeQL(analyze)均已通过,麻烦再审一次,谢谢。

@Fectivnfy112357
Fectivnfy112357 marked this pull request as ready for review August 17, 2026 04:34
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@Fectivnfy112357 Fectivnfy112357 closed this by deleting the head repository Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants