Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Hadi
2026-05-20 19:31:31 +02:00
parent 6e673f5c11
commit 44b3c67a37
6 changed files with 349 additions and 270 deletions
+2
View File
@@ -8,6 +8,7 @@
hooks = { hooks = {
gofmt.enable = true; gofmt.enable = true;
govet.enable = true; govet.enable = true;
stylua.enable = true;
gomod2nix = { gomod2nix = {
enable = true; enable = true;
@@ -53,6 +54,7 @@ in
go go
python3 python3
doctoc doctoc
stylua
trufflehog trufflehog
gomod2nixPkgs.gomod2nix gomod2nixPkgs.gomod2nix
] ]
+3 -1
View File
@@ -27,7 +27,9 @@ function on_config()
if trimmed ~= "" then if trimmed ~= "" then
if trimmed:sub(1, 1) == "!" then if trimmed:sub(1, 1) == "!" then
local ip = trimmed:sub(2):match("^%s*(.-)%s*$") local ip = trimmed:sub(2):match("^%s*(.-)%s*$")
if ip ~= "" then table.insert(blacklist, ip) end if ip ~= "" then
table.insert(blacklist, ip)
end
else else
table.insert(whitelist, trimmed) table.insert(whitelist, trimmed)
end end
+27 -9
View File
@@ -52,7 +52,9 @@ function on_config()
blacklist_req, whitelist_req = {}, {} blacklist_req, whitelist_req = {}, {}
blacklist_hist, whitelist_hist = {}, {} blacklist_hist, whitelist_hist = {}, {}
local cfg = get_config() local cfg = get_config()
if not cfg or not cfg.patterns then return end if not cfg or not cfg.patterns then
return
end
for _, line in ipairs(cfg.patterns) do for _, line in ipairs(cfg.patterns) do
local trimmed = line:match("^%s*(.-)%s*$") local trimmed = line:match("^%s*(.-)%s*$")
if trimmed ~= "" and trimmed:sub(1, 1) ~= "#" then if trimmed ~= "" and trimmed:sub(1, 1) ~= "#" then
@@ -73,17 +75,27 @@ end
local function check_skip(url, bl_extra, wl_extra) local function check_skip(url, bl_extra, wl_extra)
for _, p in ipairs(blacklist) do for _, p in ipairs(blacklist) do
if url:match(p) then return true end if url:match(p) then
return true
end
end end
for _, p in ipairs(bl_extra) do for _, p in ipairs(bl_extra) do
if url:match(p) then return true end if url:match(p) then
return true
end
end end
local wl = {} local wl = {}
for _, p in ipairs(whitelist) do wl[#wl + 1] = p end for _, p in ipairs(whitelist) do
for _, p in ipairs(wl_extra) do wl[#wl + 1] = p end wl[#wl + 1] = p
end
for _, p in ipairs(wl_extra) do
wl[#wl + 1] = p
end
if #wl > 0 then if #wl > 0 then
for _, p in ipairs(wl) do for _, p in ipairs(wl) do
if url:match(p) then return false end if url:match(p) then
return false
end
end end
return true return true
end end
@@ -91,13 +103,19 @@ local function check_skip(url, bl_extra, wl_extra)
end end
function on_request(req) function on_request(req)
if check_skip(req.url, blacklist_req, whitelist_req) then return "forward" end if check_skip(req.url, blacklist_req, whitelist_req) then
return "forward"
end
end end
function on_response(req) function on_response(req)
if check_skip(req.url, blacklist_req, whitelist_req) then return "forward" end if check_skip(req.url, blacklist_req, whitelist_req) then
return "forward"
end
end end
function on_history_entry(entry) function on_history_entry(entry)
if check_skip(entry.host .. entry.path, blacklist_hist, whitelist_hist) then return "skip" end if check_skip(entry.host .. entry.path, blacklist_hist, whitelist_hist) then
return "skip"
end
end end
+74 -28
View File
@@ -21,40 +21,61 @@ local CONTENT_TYPES = {
-- Key name alternation (case-insensitive via grep -i) -- Key name alternation (case-insensitive via grep -i)
-- Suffixes are required (no bare generic keyword alone). -- Suffixes are required (no bare generic keyword alone).
local KEYS = { local KEYS = {
"access(_key|_token)", "accessid_secret", "account(_key|_sid)", "access(_key|_token)",
"admin_pass(word)?", "admin_user", "accessid_secret",
"account(_key|_sid)",
"admin_pass(word)?",
"admin_user",
"(algolia|aws|gcp|azure|heroku|firebase|github|gitlab|slack|datadog|stripe|twilio|vercel|supabase|sendgrid|cloudinary|cloudflare|bitbucket|npm|netlify|auth0|okta|sentry)(_?(api|secret|access)(_?(key|token|id|sid|secret))?|_?(key|token|id|sid|secret))", "(algolia|aws|gcp|azure|heroku|firebase|github|gitlab|slack|datadog|stripe|twilio|vercel|supabase|sendgrid|cloudinary|cloudflare|bitbucket|npm|netlify|auth0|okta|sentry)(_?(api|secret|access)(_?(key|token|id|sid|secret))?|_?(key|token|id|sid|secret))",
"ansible_vault_password", "aos_key", "ansible_vault_password",
"aos_key",
"api(_key|_secret|_token)", "api(_key|_secret|_token)",
"app_(id|key|secret)", "application(_key|_id|_secret)", "app_(id|key|secret)",
"auth(_token|_secret|orization)", "authkey", "authsecret", "application(_key|_id|_secret)",
"auth(_token|_secret|orization)",
"authkey",
"authsecret",
"bearer_?token", "bearer_?token",
"bucket(_password|_key)", "bucket(_password|_key)",
"cert_?pass(word)?", "certificate_password", "cert_?pass(word)?",
"certificate_password",
"client(_id|_secret)", "client(_id|_secret)",
"codecov_token", "consumer_(key|secret)", "codecov_token",
"connection_?string", "credentials?", "crypt(_key|_secret)", "consumer_(key|secret)",
"connection_?string",
"credentials?",
"crypt(_key|_secret)",
"db_(password|passwd|user(name)?)", "db_(password|passwd|user(name)?)",
"deploy(_key|_password|_token)", "deploy(_key|_password|_token)",
"docker_?pass(word)?", "dockerhub_?password", "docker_?pass(word)?",
"dockerhub_?password",
"encryption_(key|password)", "encryption_(key|password)",
"jwt_secret", "json_web_token", "jwt_secret",
"keycloak_secret", "kubernetes_token", "json_web_token",
"ldap_(password|bindpw)", "login(_password|_token)", "keycloak_secret",
"mail_?password", "mail_smtp_pass", "kubernetes_token",
"mysql_password", "mongo_password", "ldap_(password|bindpw)",
"netlify_token", "npm(_token|_auth_token)", "login(_password|_token)",
"mail_?password",
"mail_smtp_pass",
"mysql_password",
"mongo_password",
"netlify_token",
"npm(_token|_auth_token)",
"oauth(_token|_secret)", "oauth(_token|_secret)",
"openai_(api_key|secret)", "openai_(api_key|secret)",
"pass(word)?", "passwd", "pass(word)?",
"passwd",
"private(_key|_token)", "private(_key|_token)",
"rds_password", "rds_password",
"s3(_key|_secret|_access_key_id)", "s3(_key|_secret|_access_key_id)",
"secret(_key|_token|_id)", "security_token", "secret(_key|_token|_id)",
"security_token",
"sendgrid_api_key", "sendgrid_api_key",
"ses_(smtp|access|secret)", "ses_(smtp|access|secret)",
"service(_account|_key|_token)", "service(_account|_key|_token)",
"smtp_pass(word)?", "smtp_secret", "smtp_pass(word)?",
"smtp_secret",
"sonar_token", "sonar_token",
"ssh(_key|_private_key|_rsa)", "ssh(_key|_private_key|_rsa)",
"supabase(_anon|_service)?_key", "supabase(_anon|_service)?_key",
@@ -79,10 +100,14 @@ local FULL_PAT = KEY_PAT .. '[a-z0-9._-]{0,20}[^=:a-zA-Z0-9_]{0,3}[[:space:]]*[:
local GREP_CMD = "grep -Eoni '" .. FULL_PAT .. "'" local GREP_CMD = "grep -Eoni '" .. FULL_PAT .. "'"
local function is_relevant(ct) local function is_relevant(ct)
if not ct or ct == "" then return false end if not ct or ct == "" then
return false
end
ct = ct:lower() ct = ct:lower()
for _, t in ipairs(CONTENT_TYPES) do for _, t in ipairs(CONTENT_TYPES) do
if ct:find(t, 1, true) then return true end if ct:find(t, 1, true) then
return true
end
end end
return false return false
end end
@@ -94,17 +119,23 @@ local function build_context(lines, linenum)
local before, after = {}, {} local before, after = {}, {}
for i = lo, linenum - 1 do for i = lo, linenum - 1 do
local l = lines[i] or "" local l = lines[i] or ""
if #l > 120 then l = l:sub(1, 120) .. "..." end if #l > 120 then
l = l:sub(1, 120) .. "..."
end
table.insert(before, l) table.insert(before, l)
end end
for i = linenum + 1, hi do for i = linenum + 1, hi do
local l = lines[i] or "" local l = lines[i] or ""
if #l > 120 then l = l:sub(1, 120) .. "..." end if #l > 120 then
l = l:sub(1, 120) .. "..."
end
table.insert(after, l) table.insert(after, l)
end end
local matched_line = lines[linenum] or "" local matched_line = lines[linenum] or ""
if #matched_line > 200 then matched_line = matched_line:sub(1, 200) .. "..." end if #matched_line > 200 then
matched_line = matched_line:sub(1, 200) .. "..."
end
local parts = {} local parts = {}
if #before > 0 then if #before > 0 then
@@ -118,15 +149,21 @@ local function build_context(lines, linenum)
end end
local function scan(label, ct, body, host, path) local function scan(label, ct, body, host, path)
if not is_relevant(ct) then return end if not is_relevant(ct) then
if not body or body == "" then return end return
end
if not body or body == "" then
return
end
local out, err = shell_pipe(GREP_CMD, body) local out, err = shell_pipe(GREP_CMD, body)
if err and err ~= "" then if err and err ~= "" then
log("grep error on " .. label .. " for " .. host .. path .. ": " .. err) log("grep error on " .. label .. " for " .. host .. path .. ": " .. err)
return return
end end
if not out or out == "" then return end if not out or out == "" then
return
end
local lines = {} local lines = {}
for line in (body .. "\n"):gmatch("([^\n]*)\n") do for line in (body .. "\n"):gmatch("([^\n]*)\n") do
@@ -140,11 +177,20 @@ local function scan(label, ct, body, host, path)
matched = matched:match("^%s*(.-)%s*$") matched = matched:match("^%s*(.-)%s*$")
if matched ~= "" then if matched ~= "" then
local display = matched local display = matched
if #display > 200 then display = display:sub(1, 200) .. "..." end if #display > 200 then
display = display:sub(1, 200) .. "..."
end
local ctx = build_context(lines, linenum) local ctx = build_context(lines, linenum)
create_finding({ create_finding({
title = "Potential secret in " .. label .. " (" .. host .. ")", title = "Potential secret in " .. label .. " (" .. host .. ")",
description = "**Host:** `" .. host .. "` \n**Path:** `" .. path .. "`\n\n**Match:** `" .. display .. "`\n\n" .. ctx, description = "**Host:** `"
.. host
.. "` \n**Path:** `"
.. path
.. "`\n\n**Match:** `"
.. display
.. "`\n\n"
.. ctx,
key = host .. "|" .. path .. "|" .. label .. "|" .. matched, key = host .. "|" .. path .. "|" .. label .. "|" .. matched,
severity = "high", severity = "high",
}) })
+16 -5
View File
@@ -24,24 +24,35 @@ function on_start()
end end
local function scan(label, content, host, path) local function scan(label, content, host, path)
if not content or content == "" then return end if not content or content == "" then
local out, err = shell_pipe("f=$(mktemp) && cat > \"$f\" && trufflehog filesystem --no-color \"$f\"; rc=$?; rm -f \"$f\"; exit $rc", content) return
end
local out, err = shell_pipe(
'f=$(mktemp) && cat > "$f" && trufflehog filesystem --no-color "$f"; rc=$?; rm -f "$f"; exit $rc',
content
)
if err and err ~= "" then if err and err ~= "" then
log("trufflehog error on " .. label .. ": " .. err) log("trufflehog error on " .. label .. ": " .. err)
return return
end end
if not out or out == "" then return end if not out or out == "" then
return
end
local blocks = {} local blocks = {}
local current = nil local current = nil
for line in out:gmatch("[^\n]+") do for line in out:gmatch("[^\n]+") do
if line:match("^Found ") then if line:match("^Found ") then
if current then table.insert(blocks, current) end if current then
table.insert(blocks, current)
end
current = line current = line
elseif current then elseif current then
current = current .. "\n" .. line current = current .. "\n" .. line
end end
end end
if current then table.insert(blocks, current) end if current then
table.insert(blocks, current)
end
for _, block in ipairs(blocks) do for _, block in ipairs(blocks) do
create_finding({ create_finding({
title = "Secret detected in " .. label .. " (" .. host .. ")", title = "Secret detected in " .. label .. " (" .. host .. ")",