logredact

package
v0.23.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package logredact 日志/审计载荷凭据脱敏:连接串/URL/头中的凭据在落日志前模式化打码。 与 fence(Markdown/HTML 注入卫生)正交——本包打码凭据。

规则表是**公开且可组合**的:Rules() 给出内置表副本,RedactWith / NewRedactor 接受调用方自己的扩展规则。下游各家的凭据形态不一样(厂商前缀族、裸 hex、 任意 scheme 的 URL…),把表关在包内就等于逼下游**重写一整份脱敏器**, 或在后面串第二道——两道顺序敏感、掩码风格还不一致。

Index

Constants

View Source
const DefaultHintKeep = 4

DefaultHintKeep 保定位线索掩码默认保留位数(首尾各)。

View Source
const HintMinLenRatio = 4

HintMinLenRatio 被掩值长度不足 keep*该倍数 时**全量打码**。

阈值的作用是「留了也别等于没留」:9 字符的 secret 留首尾 4 个 = 8 个字符, 换个字符就复原了——那不是定位线索,是泄露。保留的字符数必须远小于被藏起的 字符数,掩码才真的不可逆。

Variables

This section is empty.

Functions

func IsCredentialKey added in v0.19.0

func IsCredentialKey(key string) bool

IsCredentialKey 内置键名判定(结构化键名面):键名是否命中内置凭证词表。 供集成方在自持管线里复用(jumpserver-kit 此前自写 isCredentialKey 兜 authorization/bearer/cookie/key 形态,oagentkit/guardx#51——上游暴露谓词 后可只补自己的增量词表)。

func MaskHint added in v0.6.0

func MaskHint(s string) string

MaskHint 保留首尾各 DefaultHintKeep 字符的不可逆掩码。

输出形如 "sk-a****mnop":长度**固定**(与原值长度无关), 保留位**封顶**(与原值长度无关),中间恒为固定长度的星号。

长度不足时返回全量打码 "****"(理由见 HintMinLenRatio)。 空串返回空串。

func MaskHintWith added in v0.6.0

func MaskHintWith(s string, keep int) string

MaskHintWith 保留首尾各 keep 字符的不可逆掩码。

keep<=0 取 DefaultHintKeep。被掩值长度不足 keep*HintMinLenRatio 时全量打码。 空串返回空串。

func NewPayloadRedactor added in v0.17.0

func NewPayloadRedactor(fields []string, extra []Rule) func(string) string

NewPayloadRedactor 返回可复用的「载荷裁剪 + 形状脱敏」函数(字段集与规则表 在构造时**快照**一次)。

与 NewRedactor 同构:高频路径(日志热路径、每条 tool_call 都过一遍)用它, 别每次调用现编正则。返回的函数是并发安全的(只读快照,不改共享状态)。

func NewRedactor added in v0.6.0

func NewRedactor(extra []Rule) func(string) string

NewRedactor 返回一个可复用的脱敏函数(内置规则 + 扩展规则)。

并集顺序与 RedactWith 同一契约(guardx#75):先内置、后扩展,快照里的顺序 就是应用顺序;扩展规则见已掩文本、依赖内置保留的结构锚点,详见 RedactWith。

规则表在构造时**快照**一次:调用方之后改自己的切片,不影响已构造的脱敏器。 返回的函数是并发安全的(只读规则表,不改共享状态)。

高频路径(日志热路径、每条 tool_call 都过一遍)用这个,别每次重建规则表。

func Redact

func Redact(s string) string

Redact 返回打码后的文本(无命中原样返回)。

等价于 RedactWith(s, nil):只跑内置规则。

func RedactJSON added in v0.18.0

func RedactJSON(data []byte) []byte

RedactJSON 对 JSON 载荷做结构化脱敏,输出恒为合法 JSON:

  • 合法 JSON:解码(json.Number 保数字字面量)→ 键感知递归打码 (凭证词键的**整值**打码为 "****",字符串值过文本规则兜内嵌连接串/ JWT/Bearer)→ 重新序列化。键的声明序经 map 归一后按字母序输出—— 打码保的是**结构合法**,不是字节保形。
  • 非法 JSON:回落文本规则(与 Redact 同口径)——不是错误,调用方无须 区分两条路径的返回值。

场景:结构化日志行/事件载荷在采集侧要过 JSON 解析器(Loki/ELK/事件面), 文本规则打码后的 `"api_key=****}` 会让整行降级为 raw 文本、字段索引全丢 ——排查方向被带偏到采集管道(oagentkit/guardx#44)。

func RedactJSONOpts added in v0.19.0

func RedactJSONOpts(data []byte, opts ValueOptions) []byte

RedactJSONOpts 同 RedactJSON,接受 ValueOptions(自定义键名判定、值模式 扩展规则等;非法 JSON 回落文本规则时 ExtraRules 同样生效)。

func RedactPayloadValue added in v0.17.0

func RedactPayloadValue(v any, fields ...string) any

RedactPayloadValue 结构化形态:递归把 fields 指定的字段裁剪成「只记长度」的标记, 其余字符串按 RedactValue 的口径做形状脱敏(含凭证词键的整值打码——键感知 走 redactWalk 同一份判定)。

与 RedactPayloads 语义一致(同一份判定表),差别只在形态:它吃已解码的值、 吐已解码的值,可用于「按字段名裁剪后再落库」的结构化载荷管道。

裁剪只作用于**字符串形态**的字段值;非字符串形态(对象/数组/数字)的值 原样保留,只递归下探——被裁掉的是「载荷」,不是「字段所在的那个对象」。

func RedactPayloads added in v0.17.0

func RedactPayloads(s string, fields ...string) string

RedactPayloads 把 fields 指定的载荷字段裁剪成「只记长度」的标记,再做形状脱敏。

**顺序由本函数保证**:载荷裁剪先于形状规则(见 payloadMarkerFormat 的说明)。 这正是 issue 里说的「「先裁剪再脱敏」应当由函数内部保证,而不是由调用点的 书写顺序保证」——顺序写反不报错,只是报出来的大小是错的。

fields 是**字段名白名单**(如 "content" / "content_base64" / "body"): 只裁剪这些字段的值,其余原样。刻意不给默认表——「哪些字段算载荷」是 调用方的口径,库里替它猜就等于替它决定什么会落盘。

裁剪只作用于**字符串形态**的字段值(载荷就是字符串:一段 base64、一份内容)。 对象/数组形态的值本函数不动它(那是结构化入口 RedactPayloadValue 的射程)。

高频路径(日志热路径、每条 tool_call 都过一遍)用 NewPayloadRedactor, 别每次重建正则。

func RedactPayloadsWith added in v0.17.0

func RedactPayloadsWith(s string, fields []string, extra []Rule) string

RedactPayloadsWith = RedactPayloads + 扩展规则。

扩展规则仍然排在**最后**(与 RedactWith 的口径一致:先内建、后扩展); 载荷裁剪仍在最前。三段的顺序是:载荷裁剪 → 内建规则 → 扩展规则。

func RedactSecrets

func RedactSecrets(s string, secrets ...string) string

RedactSecrets 抹除调用方已知的确切秘密(clone URL 内嵌 token、动态签发的临时 凭据等):与模式化 Redact 互补——Redact 靠规则打码未知形态的凭据,本函数抹除 已知值。与 Masker 相反,高敏感秘密不做令牌、不回填(Masker 只收低敏感拓扑标识)。 长秘密优先替换(防短串是长串前缀时留下尾段泄漏),空串忽略。

func RedactValue

func RedactValue(v any) any

RedactValue 递归脱敏任意 JSON 形态值:凭证词键的整值打码(键感知, jsonredact.go),其余 map/slice 逐字段下探,字符串过 Redact,其余原样。 事件载荷入库前的统一出口——tool_call 参数与 tool_result 文本常含连接串、 webhook key、Bearer 头。

⚠️ v0.14.0 起键感知(oagentkit/guardx#44 同源缺陷):此前只有字符串值过 文本规则——而文本规则靠 `password=xxx` 的**键值形态**定位,解码后的值 "abc123" 早已失去键上下文 ⇒ `{"password": "abc123"}` 原样透传(泄漏)。 键感知打码补上这层:键名命中凭证词表(jsonCredentialKeyRe)即整值打码。

⚠️ guardx#63/#76 起类型化容器同一下探:map[string]string、[]string、 []map[string]any、struct 导出字段(字段名过键名判定)经反射递归——此前 类型开关只认 any 解码形态,类型化容器静默漏脱(脱敏器最危险的失效形态)。 边界:未导出字段反射不可读(原样,请传导出形态);json.Number 特赦 (数字字面量保形);[]byte 是二进制不是文本(原样)。输出为副本,入参不动。

func RedactValueOpts added in v0.19.0

func RedactValueOpts(v any, opts ValueOptions) any

RedactValueOpts 同 RedactValue,接受 ValueOptions(自定义键名判定等)。

func RedactWith added in v0.6.0

func RedactWith(s string, extra []Rule) string

RedactWith 用内置规则 + 扩展规则脱敏。

**并集顺序契约**(guardx#75:两个消费仓各自用注释钉死的顺序纪律,在此升格 为可依赖的契约,不是实现细节):

  • 顺序固定为「先内置、后扩展」,每组内部按声明顺序。需要「扩展先行」的 调用方(形态比内置更精确、要抢在粗规则前保线索)用函数组合实现: 先跑自己的规则,再过 RedactWith(s, nil)——此时扩展先见原文,与内置的 幂等性由调用方自行保证。
  • 扩展规则看到的是内置规则处理**之后**的文本:内置打码保结构(scheme、 用户名、键名留着),扩展规则仍能锚定这些结构;但别指望拿到原始凭据—— 内置已命中的部分早成 ****。
  • 二次打码幂等:内置掩码是固定字面 ****,对已掩文本重复 Redact 不再改动; 扩展规则也应满足同款幂等(对 **** 无操作的规则天然满足)。
  • 定位线索保留:MaskStyleHint 档的线索只作用于捕获组(Group>0),组捕获 失败回退全量打码——既不丢「认得出哪条凭据」的能力,也不留凭据尾巴。

为什么是这个顺序:内置规则的打码**保留结构**("$1://$2:****@" —— scheme 与 用户名留着,只有凭据体被替换),它对同一段文本的定位比扩展规则精确。 先跑内置,扩展规则面对的就是「已经打过码」的文本,既不会把内置已处理的凭据 再脱一次(幂等),也不会让扩展的粗规则先吃掉内置能精确定位的那部分。

扩展规则里 Re 为 nil 的会被跳过(不 panic)——扩展表是调用方给的, 一条写错的规则不该让整条脱敏链路崩掉。

Types

type MaskStyle added in v0.6.0

type MaskStyle int

MaskStyle 命中后的替换方式。

const (
	// MaskStyleLiteral 按 Repl 字面替换(支持 $1 引用)。内置规则用它**保留结构**:
	// scheme、用户名、键名留着,只有凭据体变成 ****——处置后的文本仍能看出
	// 「是哪一条凭据」。这是默认档。
	MaskStyleLiteral MaskStyle = iota
	// MaskStyleHint 保定位线索的不可逆掩码:保留首尾各 Keep 个字符。
	// 用于「全量打码会让人认不出是哪一条凭据」的场景。
	//
	// 与 MaskStyleLiteral 是**不同档**,不是替代关系:前者给的是「认不出」,
	// 后者给的是「认得出但不还原」。
	MaskStyleHint
)

type Masker

type Masker struct {
	// contains filtered or unexported fields
}

Masker 低敏感掩码+令牌回填器(K8sGPT anonymize 形态,bianque spec 2026-09-14-aiops-uplift-design §3):把目标机拓扑标识(IPv4 + 调用方注册的精确串) 在进 LLM 前替换为 «Tn» 令牌,在最终展示面 Restore 回填原名——LLM 解释里引用的 「主机 «T1»」在报告中还原为真名,可读性与隐私兼得。

分级纪律:高敏感(密码/密钥/连接串/Bearer)不走本机制——那是 Redact 的领域, 删除后永不回填;Masker 只收低敏感拓扑标识,且回填只发生在展示面、不回灌二次 LLM 输入(防掩码词表被 prompt 注入探测绕过)。令牌用全角书名号+序号,难以仿造; Restore 对未知令牌原样保留(不猜、不误回填)。

func NewMasker

func NewMasker() *Masker

NewMasker 构造(零值不可用,一律经本构造)。

func (*Masker) AddSecret

func (m *Masker) AddSecret(v string) string

AddSecret 注册精确串(会话主机名、实例名等调用方已知标识),返回其令牌。空串忽略。

func (*Masker) Count

func (m *Masker) Count() int

Count 已签发令牌数(测试/观测用)。

func (*Masker) Mask

func (m *Masker) Mask(s string) string

Mask 掩码文本:先精确注册串,再 IPv4 模式(动态注册,同址恒同令牌)。 结构化数据的双视图入口见 MaskValue(逐字符串值 walk,无序列化往返)。

func (*Masker) MaskValue added in v0.19.0

func (m *Masker) MaskValue(v any) any

MaskValue 递归掩码任意 JSON 形态值(结构化双视图,oagentkit/guardx#54): 键不动、**逐字符串值**走 Mask——替代「json.Marshal → Mask → json.Unmarshal」 的序列化往返。往返形态的正确性依赖两个未经类型化保证的假设(令牌只在 字符串值里 / 字符串替换不破坏 JSON 结构),而同包 Redact 已实证结构破坏 面(guardx#44)——逆向 Restore 的同款结构假设不再被要求。map/slice 下探, 标量原样;输出的 map 为副本(不污染入参)。与 RedactValueOpts 同构的 walk 形态,语义是 Mask(低敏感拓扑标识)不是 Redact(高敏感不回填)。

func (*Masker) Restore

func (m *Masker) Restore(s string) string

Restore 回填令牌为原文(展示面专用;未知令牌原样保留)。结构化数据的 逆视图见 UnmaskValue。

func (*Masker) UnmaskValue added in v0.19.0

func (m *Masker) UnmaskValue(v any) any

UnmaskValue 递归回填任意 JSON 形态值中的令牌(MaskValue 的逆视图):逐 字符串值 Restore,键不动、结构不动——展示面拿到的是已反序列化的结构, 不再有「字符串替换破坏 JSON 文本」的假设面。

type Rule added in v0.6.0

type Rule struct {
	// Name 规则名(诊断用:扩展规则出错时要能指出是哪一条)。
	Name string
	// Re 命中模式。nil 的规则会被跳过(不 panic)。
	Re *regexp.Regexp
	// Repl 字面替换串,支持 $1 引用。Style=MaskStyleHint 时被忽略。
	Repl string
	// Style 替换方式,默认 MaskStyleLiteral。
	Style MaskStyle
	// Keep Style=MaskStyleHint 时保留的首尾字符数(<=0 取 DefaultHintKeep)。
	Keep int
	// Group Style=MaskStyleHint 时,掩码作用在**第几个捕获组**(1 起);0 = 整段匹配。
	//
	// 默认 0(整段)有个陷阱:规则的匹配范围里若含上下文(如 "password=xxx"),
	// 整段掩码保留的尾部 4 个字符**就是凭据的一部分**——那不是定位线索,是泄露。
	// 所以凭据类规则应当用捕获组圈出凭据本体,再把 Group 指过去。
	//
	// 组捕获失败(Group 越界、可选组未参与匹配)时回退**全量打码**而非整段
	// hint——整段 hint 的尾 4 字符落在凭据上,正是本注释警告的陷阱本身
	// (guardx#69):宁可丢线索,不留尾巴。
	Group int
}

Rule 一条脱敏规则。

命中后按 Style 替换:

MaskStyleLiteral —— 整段替换为 Repl(字面,支持 $1/$2 引用);
MaskStyleHint    —— 整段替换为保留首尾各 Keep 字符的不可逆掩码(Repl 被忽略)。

规则表按声明顺序应用;**同一条规则内的多个匹配**由 regexp 一次性替换。

func Rules added in v0.6.0

func Rules() []Rule

Rules 返回内置规则表的**副本**。

返回副本而不是共享切片:调用方要能自由改自己的表(追加规则、改顺序), 共享切片的话改到一半就污染了全进程的内置规则——而这份表是所有日志出口共用的。 规则里的 *regexp.Regexp 是不可变的,共享指针是安全的。

type ValueOptions added in v0.19.0

type ValueOptions struct {
	// IsCredentialKey 追加的键名判定:返回 true 的键整值打码(**叠加**于
	// 内置词表之上;返回 false 不豁免内置命中——要豁免请走自持 Redactor,
	// 内置词表的「值必被打码」承诺不因回调而松动)。nil = 仅内置词表。
	// (oagentkit/guardx#51:集成方喂自己的 isCredentialKey,替掉为兜自家
	// 键形态而维护的整套绕行脱敏器。)
	IsCredentialKey func(key string) bool
	// ExtraRules 追加到字符串叶子文本规则的扩展表(guardx#76 的**值模式**
	// 检测缝):结构化路径的每个字符串叶子按「先内置、后扩展」过
	// RedactWith——手机号/身份证这类**值本身即敏感、无键名可依**的形态由
	// 调用方注入正则规则,不必再自研结构递归器(agent-harness 整包 pii 的
	// 递归 ApplyValue 由此收编,只留值模式正则)。刻意不进内置表:值模式
	// 是地区/产品口径(大陆手机号正则会误掩 11 位订单号/毫秒时间戳),
	// 内置表守的是通用凭证语义。
	ExtraRules []Rule
}

ValueOptions RedactValue / RedactJSON 结构化路径的行为选项(零值 = 内置 词表语义)。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL