You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
garble/line_obfuscator.go

115 lines
2.9 KiB
Go

// Copyright (c) 2020, The Garble Authors.
// See LICENSE for licensing information.
package main
import (
"fmt"
"go/ast"
mathrand "math/rand"
"strings"
"golang.org/x/tools/go/ast/astutil"
)
const (
// PosMax is the largest line or column value that can be represented without loss.
// Source: https://go.googlesource.com/go/+/refs/heads/master/src/cmd/compile/internal/syntax/pos.go#11
PosMax = 1 << 30
// PosMin is the smallest correct value for the line number.
// Source: https://go.googlesource.com/go/+/refs/heads/master/src/cmd/compile/internal/syntax/parser_test.go#229
PosMin = 1
)
func prependComment(group *ast.CommentGroup, comment *ast.Comment) *ast.CommentGroup {
if group == nil {
return &ast.CommentGroup{List: []*ast.Comment{comment}}
}
group.List = append([]*ast.Comment{comment}, group.List...)
return group
}
// Remove all comments from CommentGroup except //go: directives.
func clearCommentGroup(group *ast.CommentGroup) *ast.CommentGroup {
if group == nil {
return nil
}
var comments []*ast.Comment
for _, comment := range group.List {
if strings.HasPrefix(comment.Text, "//go:") {
comments = append(comments, &ast.Comment{Text: comment.Text})
}
}
if len(comments) == 0 {
return nil
}
return &ast.CommentGroup{List: comments}
}
// Remove all comments from Doc (if any) except //go: directives.
func clearNodeComments(node ast.Node) {
switch n := node.(type) {
case *ast.Field:
n.Doc = clearCommentGroup(n.Doc)
case *ast.ImportSpec:
n.Doc = clearCommentGroup(n.Doc)
case *ast.ValueSpec:
n.Doc = clearCommentGroup(n.Doc)
case *ast.TypeSpec:
n.Doc = clearCommentGroup(n.Doc)
case *ast.GenDecl:
n.Doc = clearCommentGroup(n.Doc)
case *ast.FuncDecl:
n.Doc = clearCommentGroup(n.Doc)
case *ast.File:
n.Doc = clearCommentGroup(n.Doc)
}
}
func findBuildTags(commentGroups []*ast.CommentGroup) (buildTags []string) {
for _, group := range commentGroups {
for _, comment := range group.List {
if !strings.Contains(comment.Text, "+build") {
continue
}
buildTags = append(buildTags, comment.Text)
}
}
return buildTags
}
func transformLineInfo(file *ast.File) ([]string, *ast.File) {
// Save build tags and add file name leak protection
extraComments := append(findBuildTags(file.Comments), "", "//line :1")
file.Comments = nil
newLines := mathrand.Perm(len(file.Decls))
funcCounter := 0
pre := func(cursor *astutil.Cursor) bool {
node := cursor.Node()
clearNodeComments(node)
funcDecl, ok := node.(*ast.FuncDecl)
if !ok {
return true
}
if envGarbleTiny {
funcDecl.Doc = prependComment(funcDecl.Doc, &ast.Comment{Text: "//line :1"})
return true
}
comment := &ast.Comment{Text: fmt.Sprintf("//line %c.go:%d", nameCharset[mathrand.Intn(len(nameCharset))], 1+newLines[funcCounter])}
funcDecl.Doc = prependComment(funcDecl.Doc, comment)
funcCounter++
return true
}
return extraComments, astutil.Apply(file, pre, nil).(*ast.File)
}