mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-31 22:15:13 +08:00
🎈 Sync 2026-09-29 22:47:56
This commit is contained in:
1 parent
97287119b0
commit
4c053b8bdb
29 files changed
+1015
-157
No files matched your search
@@ -31,5 +31,110 @@ check("injection userinfo", http.parse_proxy("http://user;ls@192.168.5.1:1080")
|
||||
local _, e = http.parse_proxy("ftp://x:80")
|
||||
check("err is string", type(e) == "string")
|
||||
|
||||
-- ---------- wget 后端代理能力(§12 禁止 silent fallback) ----------
|
||||
-- busybox wget 只能通过 http_proxy/https_proxy 使用 http(s) 代理;
|
||||
-- socks* 必须明确报错,而不是丢掉代理直连。
|
||||
check("wget env empty proxy", http.wget_proxy_env("") == "")
|
||||
check("wget env nil proxy", http.wget_proxy_env(nil) == "")
|
||||
local env_http = http.wget_proxy_env("http://192.168.5.1:8080")
|
||||
check("wget env http proxy", type(env_http) == "string" and env_http:find("http_proxy=", 1, true) ~= nil)
|
||||
local env_https = http.wget_proxy_env("https://proxy.example.com:8443")
|
||||
check("wget env https proxy", type(env_https) == "string" and env_https:find("http_proxy=", 1, true) ~= nil)
|
||||
|
||||
local e5, m5 = http.wget_proxy_env("socks5://192.168.5.1:1080")
|
||||
check("wget rejects socks5 (no silent direct)", e5 == nil)
|
||||
check("wget socks5 err mentions SOCKS5", type(m5) == "string" and m5:find("SOCKS5", 1, true) ~= nil)
|
||||
local e4 = http.wget_proxy_env("socks4://192.168.5.1:1080")
|
||||
check("wget rejects socks4", e4 == nil)
|
||||
local eh = http.wget_proxy_env("socks5h://192.168.5.1:1080")
|
||||
check("wget rejects socks5h", eh == nil)
|
||||
|
||||
-- ---------- 日志脱敏(§39) ----------
|
||||
check("redact proxy strips credentials",
|
||||
http.redact_proxy("http://user:pass@192.168.5.1:8080") == "http://192.168.5.1:8080")
|
||||
check("redact proxy keeps credentials-free addr",
|
||||
http.redact_proxy("socks5://192.168.5.1:1080") == "socks5://192.168.5.1:1080")
|
||||
check("redact proxy handles garbage", http.redact_proxy("nonsense") == "***")
|
||||
check("redact proxy handles nil", http.redact_proxy(nil) == "***")
|
||||
|
||||
local scrubbed = http.scrub_credentials("curl: (7) failed http://user:pw@example.com/sub?token=abc")
|
||||
check("scrub removes url credentials", scrubbed:find("user:pw", 1, true) == nil)
|
||||
check("scrub keeps the rest", scrubbed:find("example.com", 1, true) ~= nil)
|
||||
check("scrub leaves clean text alone",
|
||||
http.scrub_credentials("HTTP 错误 404") == "HTTP 错误 404")
|
||||
|
||||
-- ---------- 重定向链复检(§14/§16) ----------
|
||||
-- 纯函数,不触网:日志文本按 wget -S 的真实输出格式构造
|
||||
local function chain(...)
|
||||
local lines = {}
|
||||
for _, loc in ipairs({ ... }) do
|
||||
lines[#lines + 1] = " HTTP/1.1 302 Found"
|
||||
lines[#lines + 1] = " Location: " .. loc
|
||||
end
|
||||
lines[#lines + 1] = " HTTP/1.1 200 OK"
|
||||
return table.concat(lines, "\n")
|
||||
end
|
||||
|
||||
local BASE = "http://sub.example.com/api/sub"
|
||||
check("chain public -> public allowed",
|
||||
http.validate_redirect_chain(BASE, chain("http://cdn.example.com/a")) == true)
|
||||
check("chain no redirect allowed",
|
||||
http.validate_redirect_chain(BASE, " HTTP/1.1 200 OK") == true)
|
||||
check("chain relative location allowed",
|
||||
http.validate_redirect_chain(BASE, chain("/other/path")) == true)
|
||||
|
||||
-- 公网 → 内网:必须拒绝
|
||||
local ok1, r1 = http.validate_redirect_chain(BASE, chain("http://127.0.0.1:8080/evil"))
|
||||
check("chain to 127.0.0.1 rejected", ok1 == false)
|
||||
check("chain 127.0.0.1 reason set", type(r1) == "string" and #r1 > 0)
|
||||
check("chain to localhost rejected",
|
||||
http.validate_redirect_chain(BASE, chain("http://localhost/evil")) == false)
|
||||
check("chain to 192.168.1.1 rejected",
|
||||
http.validate_redirect_chain(BASE, chain("http://192.168.1.1/evil")) == false)
|
||||
check("chain to 10.0.0.1 rejected",
|
||||
http.validate_redirect_chain(BASE, chain("http://10.0.0.1/evil")) == false)
|
||||
check("chain to 172.16.0.1 rejected",
|
||||
http.validate_redirect_chain(BASE, chain("http://172.16.0.1/evil")) == false)
|
||||
check("chain to 169.254.169.254 rejected",
|
||||
http.validate_redirect_chain(BASE, chain("http://169.254.169.254/latest/meta-data/")) == false)
|
||||
check("chain to ipv6 loopback rejected",
|
||||
http.validate_redirect_chain(BASE, chain("http://[::1]/evil")) == false)
|
||||
check("chain to ipv6 link-local rejected",
|
||||
http.validate_redirect_chain(BASE, chain("http://[fe80::1]/evil")) == false)
|
||||
-- 协议相对地址(//host/path)主机必须取自 Location,不能被当成同主机路径
|
||||
check("chain protocol-relative to private rejected",
|
||||
http.validate_redirect_chain(BASE, chain("//127.0.0.1/evil")) == false)
|
||||
check("chain protocol-relative to public allowed",
|
||||
http.validate_redirect_chain(BASE, chain("//cdn.example.com/a")) == true)
|
||||
-- 多跳:只要有一跳不安全就整体拒绝
|
||||
check("chain multi-hop second hop private rejected",
|
||||
http.validate_redirect_chain(BASE,
|
||||
chain("http://cdn.example.com/a", "http://192.168.1.1/b")) == false)
|
||||
check("chain multi-hop all public allowed",
|
||||
http.validate_redirect_chain(BASE,
|
||||
chain("http://cdn.example.com/a", "http://cdn2.example.com/b")) == true)
|
||||
-- 非法 Location
|
||||
check("chain invalid location rejected",
|
||||
http.validate_redirect_chain(BASE, chain("ftp://example.com/x")) == false)
|
||||
|
||||
-- 用 busybox wget 1.37 `-S` 的真实输出(逐字节捕获)锁定日志格式,
|
||||
-- 防止上游改变输出格式后校验静默失效
|
||||
local REAL_LOG = [[Connecting to 127.0.0.1:18080 (127.0.0.1:18080)
|
||||
HTTP/1.1 302 Found
|
||||
Server: BaseHTTP/0.6 Python/3.14.4
|
||||
Date: Tue, 29 Sep 2026 11:42:19 GMT
|
||||
Location: http://127.0.0.1:18081/
|
||||
Connecting to 127.0.0.1:18081 (127.0.0.1:18081)
|
||||
HTTP/1.1 200 OK
|
||||
Server: BaseHTTP/0.6 Python/3.14.4
|
||||
Date: Tue, 29 Sep 2026 11:42:19 GMT
|
||||
Content-Length: 11
|
||||
|
||||
saving to '/tmp/probe_out.bin'
|
||||
probe_out.bin 100% |********************************| 11 0:00:00 ETA
|
||||
]]
|
||||
check("real wget -S log: private hop rejected",
|
||||
http.validate_redirect_chain(BASE, REAL_LOG) == false)
|
||||
|
||||
print(string.format("\n%d passed, %d failed", passed, failed))
|
||||
os.exit(failed == 0 and 0 or 1)
|
||||
Reference in new issue
Block a user