ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

代码骨架哈希能做什么,不能证明什么

代码骨架哈希能做什么,不能证明什么

把两个文件的AST压成一个SHA-256摘要,然后说“相似度85%”,中间缺了一整套定义。哈希能回答特定表示是否相同,不能直接回答相似程度,更不能判断代码许可是否合规。

先给“骨架”一个小而明确的定义

下面只取函数名、参数个数和结果个数。它忽略函数体、注释和变量名,因此相同摘要只表示这几个字段相同。这样的摘要适合做粗筛或提醒,不适合当成代码来源证明。

package main import ( "crypto/sha256" "fmt" "go/ast" "go/parser" "go/token" "strings" ) func arity(f *ast.FieldList) int { if f == nil { return 0 } n := 0 for _, field := range f.List { if len(field.Names) == 0 { n++ } else { n += len(field.Names) } } return n } func skeleton(src string) ([32]byte, error) { f, err := parser.ParseFile(token.NewFileSet(), "sample.go", src, 0) if err != nil { return [32]byte{}, err } var b strings.Builder for _, decl := range f.Decls { if fn, ok := decl.(*ast.FuncDecl); ok { fmt.Fprintf(&b, "%s:%d:%d;", fn.Name.Name, arity(fn.Type.Params), arity(fn.Type.Results)) } } return sha256.Sum256([]byte(b.String())), nil } func main() { a, e1 := skeleton("package p; func Add(a,b int) int {return a+b}") b, e2 := skeleton("package p; func Add(x,y int) int {return 0}") c, e3 := skeleton("package p; func Add(x int) int {return x}") if e1 != nil || e2 != nil || e3 != nil { panic("parse") } if a != b || a == c { panic("skeleton semantics") } fmt.Println("same signature, different behavior: same skeleton hash") }

图示为抽象处理流程;本例只取函数签名字段,图中摘要是缩略符号,不能作为真实校验值。

两个行为明显不同的函数得到相同摘要,并非SHA-256发生碰撞,而是进入摘要的数据原本就一样。把丢失的信息怪到哈希算法上,会让改进方向完全跑偏。

如果真的需要相似检索

需要先定义特征、归一化方式、相似度以及误报漏报成本。MinHash通常用来估计集合相似度,仍需说明集合如何构造;不能用一个函数名就替代完整方法。还需要带人工标签的样本,才能讨论阈值是否有用。

许可判断则是另一件事。发现候选片段后,应核对上游仓库、提交、许可证和使用方式。GitHub对代码引用的说明也没有把引用索引描述成所有公开代码的完整集合。没有命中不等于不存在来源,有命中也不能跳过许可义务。

这个程序仅验证了“摘要反映所选表示”的边界,没有实现MinHash、抄袭检测或法律判断。给小工具一个准确名字,往往比给它一个夸张阈值更能帮助使用者。

参考

  • Go parser
  • GitHub:Copilot与代码引用边界
返回列表