|
|
|
@ -2,22 +2,15 @@ package main
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"flag" |
|
|
|
|
"fmt" |
|
|
|
|
"go/ast" |
|
|
|
|
"go/parser" |
|
|
|
|
"go/token" |
|
|
|
|
"log" |
|
|
|
|
"os" |
|
|
|
|
"path/filepath" |
|
|
|
|
"regexp" |
|
|
|
|
"strconv" |
|
|
|
|
"strings" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
identation = " " |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type strBoolMap map[string]bool |
|
|
|
|
|
|
|
|
|
func (a *strBoolMap) String() string { |
|
|
|
@ -75,30 +68,8 @@ func (r *regexpFlag) Set(s string) error {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
|
allowedImp map[string]map[string]bool // Map of the allowed imports
|
|
|
|
|
allowedFun map[string]bool // Map of the allowed built-in functions
|
|
|
|
|
// Is necessary an array to keep all the call instances.
|
|
|
|
|
callX []nodePos // Keeps the name of the called functions and the position in the file.
|
|
|
|
|
// A map is enough for function declarations because they are unique.
|
|
|
|
|
funcDeclPkg map[string]*funcBody // Keeps the name of the function associated to its body and its position in the file.
|
|
|
|
|
allArrayTypes = true |
|
|
|
|
arraysInstances []nodePos |
|
|
|
|
forStmts []nodePos |
|
|
|
|
basicLits []nodePos |
|
|
|
|
illegals []illegal |
|
|
|
|
notAllowedArrayT []string |
|
|
|
|
predeclaredTypes = []string{"bool", "byte", "complex64", "complex128", |
|
|
|
|
"error", "float32", "float64", "int", "int8", |
|
|
|
|
"int16", "int32", "int64", "rune", "string", |
|
|
|
|
"uint", "uint8", "uint16", "uint32", "uint64", |
|
|
|
|
"uintptr", |
|
|
|
|
} |
|
|
|
|
relativeImports []string |
|
|
|
|
importPkg map[string]*pkgFunc |
|
|
|
|
pkgName []string |
|
|
|
|
allImports map[string]bool |
|
|
|
|
openImports []string |
|
|
|
|
funcOccurrences map[string]int |
|
|
|
|
allowedFun = make(map[string]map[string]bool) |
|
|
|
|
allowedRep = make(map[string]int) |
|
|
|
|
// Flags
|
|
|
|
|
noArrays bool |
|
|
|
|
noRelativeImports bool |
|
|
|
@ -106,6 +77,7 @@ var (
|
|
|
|
|
casting bool |
|
|
|
|
noFor bool |
|
|
|
|
noLit regexpFlag |
|
|
|
|
allowBuiltin bool |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// pkgFunc for all the functions of a given package
|
|
|
|
@ -125,44 +97,11 @@ type callVisitor struct {
|
|
|
|
|
Fset *token.FileSet |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type fileVisitor struct { |
|
|
|
|
funcDecl []string |
|
|
|
|
funcCalls []string |
|
|
|
|
selectExpr []string |
|
|
|
|
arrayType []nodePos |
|
|
|
|
Fset *token.FileSet |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type pkgVisitor struct { |
|
|
|
|
Fset *token.FileSet |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type impVisitor struct { |
|
|
|
|
Fset *token.FileSet |
|
|
|
|
relativeImports []string |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Get the position of the node in the file
|
|
|
|
|
type locate interface { |
|
|
|
|
getPos(ast.Node) string |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (i *impVisitor) getPos(n ast.Node) string { |
|
|
|
|
return i.Fset.Position(n.Pos()).String() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (fv *fileVisitor) getPos(n ast.Node) string { |
|
|
|
|
return fv.Fset.Position(n.Pos()).String() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (p *pkgVisitor) getPos(n ast.Node) string { |
|
|
|
|
return p.Fset.Position(n.Pos()).String() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *callVisitor) getPos(n ast.Node) string { |
|
|
|
|
return c.Fset.Position(n.Pos()).String() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type illegal struct { |
|
|
|
|
T string |
|
|
|
|
Name string |
|
|
|
@ -173,34 +112,6 @@ func (i *illegal) String() string {
|
|
|
|
|
return i.T + " " + i.Name + " " + i.Pos |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func getPkgFunc(path string, fsetPkg *token.FileSet) { |
|
|
|
|
i := &impVisitor{Fset: fsetPkg} |
|
|
|
|
p := &pkgVisitor{Fset: fsetPkg} |
|
|
|
|
pkgs, err := parser.ParseDir(fsetPkg, path, nil, parser.AllErrors) |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
fmt.Println(err.Error()) |
|
|
|
|
os.Exit(1) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for pkgname := range pkgs { |
|
|
|
|
pkg, _ := ast.NewPackage(fsetPkg, pkgs[pkgname].Files, nil, nil) |
|
|
|
|
pkgName = append(pkgName, pkgname) |
|
|
|
|
|
|
|
|
|
ast.Walk(i, pkg) |
|
|
|
|
ast.Walk(p, pkg) |
|
|
|
|
|
|
|
|
|
for _, v := range i.relativeImports { |
|
|
|
|
if isIn(v, openImports) { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
openImports = append(openImports, v) |
|
|
|
|
getPkgFunc(path+"/"+v, fsetPkg) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Returns the smallest block containing the position pos. It can
|
|
|
|
|
// return nil if `pos` is not inside any ast.BlockStmt
|
|
|
|
|
func smallestBlock(pos token.Pos, blocks []*ast.BlockStmt) *ast.BlockStmt { |
|
|
|
@ -362,6 +273,239 @@ func loadProgram(path string, load loadedSource) error {
|
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func smallestScopeContaining(pos token.Pos, path string, load loadedSource) *ast.Scope { |
|
|
|
|
pack := load[path] |
|
|
|
|
sm := smallestBlock(pos, pack.blocks) |
|
|
|
|
if sm == nil { |
|
|
|
|
return pack.pkgScope |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return pack.scopes[sm] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func lookupDefinitionObj(el element, path string, load loadedSource) *ast.Object { |
|
|
|
|
scope := smallestScopeContaining(el.pos, path, load) |
|
|
|
|
for scope != nil { |
|
|
|
|
obj := scope.Lookup(el.name) |
|
|
|
|
if obj != nil { |
|
|
|
|
return obj |
|
|
|
|
} |
|
|
|
|
scope = scope.Outer |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type visitor struct { |
|
|
|
|
fset *token.FileSet |
|
|
|
|
uses []element |
|
|
|
|
selections map[string][]*element |
|
|
|
|
arrays []*occurrence |
|
|
|
|
lits []*occurrence |
|
|
|
|
fors []*occurrence |
|
|
|
|
callRep map[string]int |
|
|
|
|
oneTime bool |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (v *visitor) getPos(n ast.Node) string { |
|
|
|
|
return v.fset.Position(n.Pos()).String() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (v *visitor) Visit(n ast.Node) ast.Visitor { |
|
|
|
|
switch t := n.(type) { |
|
|
|
|
case *ast.FuncDecl, *ast.GenDecl, *ast.AssignStmt: |
|
|
|
|
//Avoids analysing a declaration inside a declaration
|
|
|
|
|
//Since this is handle by the functions `isAllowed`
|
|
|
|
|
if v.oneTime { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
v.oneTime = true |
|
|
|
|
return v |
|
|
|
|
case *ast.BasicLit: |
|
|
|
|
if t.Kind != token.CHAR && t.Kind != token.STRING { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
v.lits = append(v.lits, &occurrence{pos: v.getPos(n), name: t.Value}) |
|
|
|
|
|
|
|
|
|
case *ast.ArrayType: |
|
|
|
|
if op, ok := t.Elt.(*ast.Ident); ok { |
|
|
|
|
v.arrays = append(v.arrays, &occurrence{ |
|
|
|
|
name: op.Name, |
|
|
|
|
pos: v.getPos(n), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
case *ast.ForStmt: |
|
|
|
|
v.fors = append(v.fors, &occurrence{ |
|
|
|
|
name: "for", |
|
|
|
|
pos: v.getPos(n), |
|
|
|
|
}) |
|
|
|
|
case *ast.CallExpr: |
|
|
|
|
if fun, ok := t.Fun.(*ast.Ident); ok { |
|
|
|
|
v.uses = append(v.uses, element{ |
|
|
|
|
name: fun.Name, |
|
|
|
|
pos: fun.Pos(), |
|
|
|
|
}) |
|
|
|
|
v.callRep[fun.Name]++ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case *ast.SelectorExpr: |
|
|
|
|
if x, ok := t.X.(*ast.Ident); ok { |
|
|
|
|
v.selections[x.Name] = append(v.selections[x.Name], &element{ |
|
|
|
|
name: t.Sel.Name, |
|
|
|
|
pos: n.Pos(), |
|
|
|
|
}) |
|
|
|
|
v.callRep[x.Name+"."+t.Sel.Name]++ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return v |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Returns the info structure with all the ocurrences of the element
|
|
|
|
|
// of the analised in the project
|
|
|
|
|
func isAllowed(function element, path string, load loadedSource, walked map[ast.Node]bool, info *info) bool { |
|
|
|
|
if walked == nil { |
|
|
|
|
walked = make(map[ast.Node]bool) |
|
|
|
|
} |
|
|
|
|
fdef := lookupDefinitionObj(function, path, load) |
|
|
|
|
if fdef == nil && !allowedFun["builtin"]["*"] && !allowedFun["builtin"][function.name] { |
|
|
|
|
info.illegals = append(info.illegals, &illegal{ |
|
|
|
|
T: "illegal-call", |
|
|
|
|
Name: function.name, |
|
|
|
|
Pos: load[path].fset.Position(function.pos).String(), |
|
|
|
|
}) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
if fdef == nil { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
if arg, ok := fdef.Data.(data); ok && arg.argument { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
funcNode := load[path].objFunc[fdef] |
|
|
|
|
v := &visitor{ |
|
|
|
|
selections: make(map[string][]*element), |
|
|
|
|
callRep: make(map[string]int), |
|
|
|
|
fset: load[path].fset, |
|
|
|
|
} |
|
|
|
|
if !walked[funcNode] { |
|
|
|
|
ast.Walk(v, funcNode) |
|
|
|
|
info.fors = append(info.fors, v.fors...) |
|
|
|
|
info.lits = append(info.lits, v.lits...) |
|
|
|
|
info.arrays = append(info.arrays, v.arrays...) |
|
|
|
|
for name, v := range v.callRep { |
|
|
|
|
info.callRep[name] += v |
|
|
|
|
} |
|
|
|
|
walked[funcNode] = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if v.uses == nil && v.selections == nil { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
allowed := true |
|
|
|
|
for _, use := range v.uses { |
|
|
|
|
allowedUse := isAllowed(use, path, load, walked, info) |
|
|
|
|
if !allowedUse { |
|
|
|
|
info.illegals = append(info.illegals, &illegal{ |
|
|
|
|
T: "illegal-call", |
|
|
|
|
Name: use.name, |
|
|
|
|
Pos: load[path].fset.Position(use.pos).String(), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
allowed = allowedUse && allowed |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for pck, funcNames := range v.selections { |
|
|
|
|
importRelPath := load[path].relImports[pck] |
|
|
|
|
for _, fun := range funcNames { |
|
|
|
|
if importRelPath == nil { |
|
|
|
|
absImp := load[path].absImports[pck] |
|
|
|
|
if absImp != nil && !allowedFun[absImp.name][fun.name] && !allowedFun[absImp.name]["*"] { |
|
|
|
|
// Add to the illegals array the import and selection
|
|
|
|
|
info.illegals = append(info.illegals, &illegal{ |
|
|
|
|
T: "illegal-access", |
|
|
|
|
Name: pck + "." + fun.name, |
|
|
|
|
Pos: load[path].fset.Position(fun.pos).String(), |
|
|
|
|
}) |
|
|
|
|
allowed = false |
|
|
|
|
} |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
newPath, err := filepath.Abs(path + "/" + importRelPath.name) |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
panic(err) |
|
|
|
|
} |
|
|
|
|
newEl := element{ |
|
|
|
|
name: fun.name, |
|
|
|
|
pos: token.Pos(0), |
|
|
|
|
} |
|
|
|
|
allowedSel := isAllowed(newEl, newPath, load, walked, info) |
|
|
|
|
if !allowedSel { |
|
|
|
|
info.illegals = append(info.illegals, &illegal{ |
|
|
|
|
T: "illegal-access", |
|
|
|
|
Name: pck + "." + fun.name, |
|
|
|
|
Pos: load[path].fset.Position(fun.pos).String(), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
allowed = allowedSel && allowed |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if !allowed { |
|
|
|
|
info.illegals = append(info.illegals, &illegal{ |
|
|
|
|
T: "illegal-definition", |
|
|
|
|
Name: fdef.Name, |
|
|
|
|
Pos: load[path].fset.Position(funcNode.Pos()).String(), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
return allowed |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func removeRepetitions(slc []*illegal) []*illegal { |
|
|
|
|
var result []*illegal |
|
|
|
|
in := make(map[string]bool) |
|
|
|
|
for _, v := range slc { |
|
|
|
|
if in[v.Pos] { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
result = append(result, v) |
|
|
|
|
in[v.Pos] = true |
|
|
|
|
} |
|
|
|
|
return result |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type occurrence struct { |
|
|
|
|
name string |
|
|
|
|
pos string |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type info struct { |
|
|
|
|
arrays []*occurrence |
|
|
|
|
lits []*occurrence |
|
|
|
|
fors []*occurrence |
|
|
|
|
callRep map[string]int |
|
|
|
|
illegals []*illegal // functions, selections that are not allowed
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func analyseProgram(functions []*fDefInfo, path string, load loadedSource) *info { |
|
|
|
|
info := &info{ |
|
|
|
|
callRep: make(map[string]int), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
walked := make(map[ast.Node]bool) |
|
|
|
|
|
|
|
|
|
for _, v := range functions { |
|
|
|
|
f := element{ |
|
|
|
|
name: v.obj.Name, |
|
|
|
|
pos: token.Pos(0), |
|
|
|
|
} |
|
|
|
|
isAllowed(f, path, load, walked, info) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
info.illegals = removeRepetitions(info.illegals) |
|
|
|
|
return info |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//reformat from the data base
|
|
|
|
|
func splitArgs(args string) []string { |
|
|
|
|
result := strings.Split(args, " ") |
|
|
|
@ -379,18 +523,6 @@ func rightFile(args string) string {
|
|
|
|
|
return "" |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func allowCastingAndImp(allowedImports []string) { |
|
|
|
|
casted := false |
|
|
|
|
for i, v := range allowedImports { |
|
|
|
|
casted = allow(v, casted) |
|
|
|
|
if v == "--no-array" { |
|
|
|
|
allArrayTypes = false |
|
|
|
|
notAllowedArrayT = append(notAllowedArrayT, allowedImports[i+1:]...) |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type flags struct { |
|
|
|
|
l struct { // flag for char or string literal
|
|
|
|
|
noLit bool // true -> unallows
|
|
|
|
@ -425,36 +557,6 @@ func removeAmount(s string) string {
|
|
|
|
|
return strRm |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// compares if the function is used a certain amount of times allowed
|
|
|
|
|
func allowedAmount(occurrences map[string]int, allowedImports []string) { |
|
|
|
|
function := "" |
|
|
|
|
funcSelector := "" |
|
|
|
|
for _, v := range allowedImports { |
|
|
|
|
// pkg in case it's a build in function and slice in case it's a selector function
|
|
|
|
|
pkg, slice := trimRelativeImport(v) |
|
|
|
|
if slice != nil { |
|
|
|
|
function = strings.Join(slice, ".") |
|
|
|
|
funcSelector = removeAmount(function) |
|
|
|
|
} else { |
|
|
|
|
function = pkg |
|
|
|
|
funcSelector = removeAmount(pkg) |
|
|
|
|
} |
|
|
|
|
if strings.ContainsAny(function, "#") { |
|
|
|
|
strNbr := strings.TrimPrefix(function, funcSelector+"#") |
|
|
|
|
nbr, err := strconv.Atoi(strNbr) |
|
|
|
|
if err != nil { |
|
|
|
|
log.Panic(err) |
|
|
|
|
} |
|
|
|
|
if occurrences[funcSelector] > nbr { |
|
|
|
|
illegals = append(illegals, illegal{ |
|
|
|
|
T: "illegal-amount", |
|
|
|
|
Name: funcSelector + " allowed count " + strNbr + " your count " + strconv.Itoa(occurrences[funcSelector]), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func init() { |
|
|
|
|
flag.Var(&noTheseArrays, "no-these-arrays", "unallowes the array types passed in the flag") |
|
|
|
|
flag.Var(&noLit, "no-lit", |
|
|
|
@ -465,68 +567,10 @@ func init() {
|
|
|
|
|
flag.BoolVar(&noFor, "no-for", false, `The "for" instruction is not allowed`) |
|
|
|
|
flag.BoolVar(&casting, "cast", false, "allowes casting") |
|
|
|
|
flag.BoolVar(&noArrays, "no-arrays", false, "unallowes the array types passed in the flag") |
|
|
|
|
flag.BoolVar(&allowBuiltin, "allow-builtin", false, "Allowes all builtin functions and casting") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func main() { |
|
|
|
|
if len(os.Args) < 2 { |
|
|
|
|
fmt.Println("No file or directory") |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var allowedImports []string |
|
|
|
|
|
|
|
|
|
if len(os.Args) > 2 { |
|
|
|
|
allowedImports = splitArgs(os.Args[2]) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
allowCastingAndImp(allowedImports) |
|
|
|
|
flag := parseFlags(allowedImports) |
|
|
|
|
|
|
|
|
|
filename := strings.TrimSpace(rightFile(os.Args[1])) |
|
|
|
|
split := strings.Split(filename, "/") |
|
|
|
|
path := strings.Join(split[:len(split)-1], "/") |
|
|
|
|
|
|
|
|
|
if path == "" { |
|
|
|
|
path = "." |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fsetFile := token.NewFileSet() |
|
|
|
|
fsetPkg := token.NewFileSet() |
|
|
|
|
|
|
|
|
|
file, err := parser.ParseFile(fsetFile, filename, nil, parser.AllErrors) |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
fmt.Println("Parsing") |
|
|
|
|
fmt.Println(err.Error()) |
|
|
|
|
os.Exit(1) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Get all the name of all functions declared in the file
|
|
|
|
|
w := &fileVisitor{Fset: fsetFile} |
|
|
|
|
ast.Walk(w, file) |
|
|
|
|
getPkgFunc(path, fsetPkg) |
|
|
|
|
|
|
|
|
|
for _, v := range w.funcDecl { |
|
|
|
|
isFuncAllowed(v, fsetPkg) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: Parsing the arguments for the --max-occurrences flag
|
|
|
|
|
allowedAmount(funcOccurrences, allowedImports) |
|
|
|
|
|
|
|
|
|
if flag != nil { |
|
|
|
|
flag.unallowLits() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
analyzeArrayT() |
|
|
|
|
analyzeForStmt(allowedImports) |
|
|
|
|
|
|
|
|
|
if len(illegals) > 0 { |
|
|
|
|
fmt.Println("Cheating") |
|
|
|
|
for _, i := range illegals { |
|
|
|
|
fmt.Println(identation + i.String()) |
|
|
|
|
} |
|
|
|
|
os.Exit(1) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type element struct { |
|
|
|
@ -635,20 +679,6 @@ func (l *loadVisitor) Visit(n ast.Node) ast.Visitor {
|
|
|
|
|
return l |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (f flags) unallowLits() { |
|
|
|
|
if f.l.noLit { |
|
|
|
|
for _, v := range basicLits { |
|
|
|
|
if !f.isLitAllowed(v.name) { |
|
|
|
|
illegals = append(illegals, illegal{ |
|
|
|
|
T: "illegal-literal", |
|
|
|
|
Name: v.name, |
|
|
|
|
Pos: v.position, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (f flags) isLitAllowed(s string) bool { |
|
|
|
|
matched, err := regexp.Match(f.l.pattern, []byte(s)) |
|
|
|
|
|
|
|
|
@ -659,448 +689,8 @@ func (f flags) isLitAllowed(s string) bool {
|
|
|
|
|
return !matched |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func analyzeForStmt(args []string) { |
|
|
|
|
if isIn("--no-for", args) { |
|
|
|
|
for _, v := range forStmts { |
|
|
|
|
illegals = append(illegals, illegal{ |
|
|
|
|
T: "illegal-loop", |
|
|
|
|
Name: v.name, |
|
|
|
|
Pos: v.position, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func analyzeArrayT() { |
|
|
|
|
if !allArrayTypes { |
|
|
|
|
l := len(notAllowedArrayT) |
|
|
|
|
for _, v := range arraysInstances { |
|
|
|
|
if l == 0 || |
|
|
|
|
isIn(v.name, notAllowedArrayT) { |
|
|
|
|
illegals = append(illegals, illegal{ |
|
|
|
|
T: "illegal-array-type", |
|
|
|
|
Name: v.name, |
|
|
|
|
Pos: v.position, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func allow(s string, casted bool) bool { |
|
|
|
|
if strings.ContainsRune(s, '.') { |
|
|
|
|
allowImport(s) |
|
|
|
|
} else { |
|
|
|
|
if allowedFun == nil { |
|
|
|
|
allowedFun = make(map[string]bool) |
|
|
|
|
} |
|
|
|
|
if strings.ContainsAny(s, "#") { |
|
|
|
|
s = removeAmount(s) |
|
|
|
|
} |
|
|
|
|
allowedFun[s] = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if s == "--cast" && !casted { |
|
|
|
|
for _, v := range predeclaredTypes { |
|
|
|
|
allow(v, false) |
|
|
|
|
} |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
return casted |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Returns true if the string matches the format of a relative import
|
|
|
|
|
func isRelativeImport(s string) bool { |
|
|
|
|
reg := regexp.MustCompile(`^\.`) |
|
|
|
|
return reg.Match([]byte(s)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Returns true if the string represents an import package
|
|
|
|
|
// i.e and expresion like <lib>.<function>
|
|
|
|
|
func isImport(s string) bool { |
|
|
|
|
matched, _ := regexp.MatchString(`.\..`, s) |
|
|
|
|
return matched |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func trimRelativeImport(str string) (string, []string) { |
|
|
|
|
var pkg string |
|
|
|
|
var slice []string |
|
|
|
|
if isImport(str) && !isRelativeImport(str) { |
|
|
|
|
splited := strings.Split(str, "/") |
|
|
|
|
slice = strings.Split(splited[len(splited)-1], ".") |
|
|
|
|
splited[len(splited)-1] = slice[0] |
|
|
|
|
pkg = strings.Join(splited, "/") |
|
|
|
|
|
|
|
|
|
if allowedImp[slice[0]] == nil { |
|
|
|
|
allowedImp[slice[0]] = make(map[string]bool) |
|
|
|
|
} |
|
|
|
|
fn := slice[len(slice)-1] |
|
|
|
|
allowedImp[slice[0]][fn] = true |
|
|
|
|
} else { |
|
|
|
|
pkg = str |
|
|
|
|
} |
|
|
|
|
return pkg, slice |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func allowImport(s string) { |
|
|
|
|
if allowedImp == nil { |
|
|
|
|
allowedImp = make(map[string]map[string]bool) |
|
|
|
|
} |
|
|
|
|
if allImports == nil { |
|
|
|
|
allImports = make(map[string]bool) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pkg, slice := trimRelativeImport(s) |
|
|
|
|
|
|
|
|
|
allImports[pkg] = true |
|
|
|
|
|
|
|
|
|
fn := "*" |
|
|
|
|
if len(slice) > 1 { |
|
|
|
|
fn = removeAmount(slice[1]) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if allowedImp[pkg] == nil { |
|
|
|
|
allowedImp[pkg] = make(map[string]bool) |
|
|
|
|
} |
|
|
|
|
allowedImp[pkg][fn] = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func addToIllegals(funcname string) { |
|
|
|
|
for _, v := range callX { |
|
|
|
|
if v.name == funcname { |
|
|
|
|
pos := v.position |
|
|
|
|
setIllegal("illegal-call", v.name, pos) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if funcDeclPkg[funcname] != nil { |
|
|
|
|
pos := funcDeclPkg[funcname].position |
|
|
|
|
setIllegal("illegal-call", funcname, pos) |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// First ignoring the imported functions
|
|
|
|
|
func isFuncAllowed(funcname string, fset *token.FileSet) bool { |
|
|
|
|
if allowedFun[funcname] { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !isFuncDeclIn(funcname, funcDeclPkg) { |
|
|
|
|
addToIllegals(funcname) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
bodyf := funcDeclPkg[funcname].body |
|
|
|
|
|
|
|
|
|
if bodyf == nil { |
|
|
|
|
addToIllegals(funcname) |
|
|
|
|
fmt.Println("Body is nil") |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
c := &callVisitor{Fset: fset} |
|
|
|
|
ast.Walk(c, bodyf) |
|
|
|
|
|
|
|
|
|
res := true |
|
|
|
|
|
|
|
|
|
for _, v := range c.Calls { |
|
|
|
|
if v == funcname { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
allowed := isFuncAllowed(v, fset) |
|
|
|
|
if !allowed { |
|
|
|
|
addToIllegals(funcname) |
|
|
|
|
for _, v := range c.Calls { |
|
|
|
|
if v == funcname { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
allowed := isFuncAllowed(v, fset) |
|
|
|
|
if !allowed { |
|
|
|
|
addToIllegals(funcname) |
|
|
|
|
} |
|
|
|
|
res = res && allowed |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
res = res && allowed |
|
|
|
|
} |
|
|
|
|
return res |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func isFuncDeclIn(funcname string, fundecl map[string]*funcBody) bool { |
|
|
|
|
return fundecl[funcname] != nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func isFuncCallIn(funcname string, funP []nodePos) bool { |
|
|
|
|
for _, v := range funP { |
|
|
|
|
if v.name == funcname { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func isIn(s string, slc []string) bool { |
|
|
|
|
for _, v := range slc { |
|
|
|
|
if s == v { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Keeps the positions of each function call and declaration
|
|
|
|
|
type funcBody struct { |
|
|
|
|
position string |
|
|
|
|
body *ast.BlockStmt |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type nodePos struct { |
|
|
|
|
position string |
|
|
|
|
name string |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (fv *fileVisitor) Visit(n ast.Node) ast.Visitor { |
|
|
|
|
fv.checkImport(n) |
|
|
|
|
if ide, ok := n.(*ast.CallExpr); ok { |
|
|
|
|
if opt, ok := ide.Fun.(*ast.Ident); ok { |
|
|
|
|
fv.funcCalls = append(fv.funcCalls, opt.Name) |
|
|
|
|
newFun := nodePos{position: fv.getPos(n), name: opt.Name} |
|
|
|
|
callX = append(callX, newFun) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if expr, ok := n.(*ast.SelectorExpr); ok { |
|
|
|
|
if x, ok := expr.X.(*ast.Ident); ok { |
|
|
|
|
fv.selectExpr = append(fv.selectExpr, x.Name+"."+expr.Sel.Name) |
|
|
|
|
|
|
|
|
|
// saves the function in to the map, from the package
|
|
|
|
|
if importPkg[x.Name] != nil { |
|
|
|
|
importPkg[x.Name].functions = append(importPkg[x.Name].functions, x.Name+"."+expr.Sel.Name) |
|
|
|
|
} |
|
|
|
|
if funcOccurrences == nil { |
|
|
|
|
funcOccurrences = make(map[string]int) |
|
|
|
|
} |
|
|
|
|
funcOccurrences[x.Name+"."+expr.Sel.Name]++ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ex, ok := n.(*ast.ArrayType); ok { |
|
|
|
|
if op, ok := ex.Elt.(*ast.Ident); ok { |
|
|
|
|
fv.arrayType = append(fv.arrayType, nodePos{ |
|
|
|
|
name: op.Name, |
|
|
|
|
position: fv.getPos(n), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if exp, ok := n.(*ast.FuncDecl); ok { |
|
|
|
|
fv.funcDecl = append(fv.funcDecl, exp.Name.Name) |
|
|
|
|
for _, v := range exp.Type.Params.List { |
|
|
|
|
if _, ok := v.Type.(*ast.FuncType); ok { |
|
|
|
|
if allowedFun == nil { |
|
|
|
|
allowedFun = make(map[string]bool) |
|
|
|
|
} |
|
|
|
|
for _, name := range v.Names { |
|
|
|
|
allowedFun[name.Name] = true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ex, ok := n.(*ast.AssignStmt); ok { |
|
|
|
|
if exp, ok := ex.Rhs[0].(*ast.FuncLit); ok { |
|
|
|
|
if ide, ok := ex.Lhs[0].(*ast.Ident); ok { |
|
|
|
|
if funcDeclPkg == nil { |
|
|
|
|
funcDeclPkg = make(map[string]*funcBody) |
|
|
|
|
} |
|
|
|
|
funcDeclPkg[ide.Name] = &funcBody{body: exp.Body, position: fv.getPos(n)} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return fv |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func positionIsIn(illegals []illegal, pos string) bool { |
|
|
|
|
for _, v := range illegals { |
|
|
|
|
if v.Pos == pos { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (p *pkgVisitor) Visit(n ast.Node) ast.Visitor { |
|
|
|
|
if exp, ok := n.(*ast.FuncDecl); ok { |
|
|
|
|
if funcDeclPkg == nil { |
|
|
|
|
funcDeclPkg = make(map[string]*funcBody) |
|
|
|
|
} |
|
|
|
|
funcDeclPkg[exp.Name.Name] = &funcBody{body: exp.Body, position: p.getPos(n)} |
|
|
|
|
for _, pkg := range pkgName { |
|
|
|
|
if importPkg[pkg] != nil && isIn(pkg+"."+exp.Name.Name, importPkg[pkg].functions) { |
|
|
|
|
funcDeclPkg[pkg+"."+exp.Name.Name] = &funcBody{body: exp.Body, position: p.getPos(n)} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ex, ok := n.(*ast.AssignStmt); ok { |
|
|
|
|
if exp, ok := ex.Rhs[0].(*ast.FuncLit); ok { |
|
|
|
|
if ide, ok := ex.Lhs[0].(*ast.Ident); ok { |
|
|
|
|
if funcDeclPkg == nil { |
|
|
|
|
funcDeclPkg = make(map[string]*funcBody) |
|
|
|
|
} |
|
|
|
|
funcDeclPkg[ide.Name] = &funcBody{body: exp.Body, position: p.getPos(n)} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return p |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func setIllegal(illegalType, funcName, pos string) { |
|
|
|
|
if !positionIsIn(illegals, pos) { |
|
|
|
|
illegals = append(illegals, illegal{ |
|
|
|
|
T: illegalType, |
|
|
|
|
Name: funcName, |
|
|
|
|
Pos: pos, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Signals that exists at least one callExpr in the node
|
|
|
|
|
func (c *callVisitor) Visit(n ast.Node) ast.Visitor { |
|
|
|
|
if id, ok := n.(*ast.BasicLit); ok { |
|
|
|
|
if id.Kind != token.CHAR && id.Kind != token.STRING { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
basicLits = append(basicLits, nodePos{position: c.getPos(n), name: id.Value}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if exp, ok := n.(*ast.CallExpr); ok { |
|
|
|
|
if fun, ok := exp.Fun.(*ast.Ident); ok { |
|
|
|
|
c.Calls = append(c.Calls, fun.Name) |
|
|
|
|
newFun := nodePos{position: c.getPos(n), name: fun.Name} |
|
|
|
|
callX = append(callX, newFun) |
|
|
|
|
if funcOccurrences == nil { |
|
|
|
|
funcOccurrences = make(map[string]int) |
|
|
|
|
} |
|
|
|
|
funcOccurrences[fun.Name]++ |
|
|
|
|
return c |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// SelectorExpr is when we access a value (dot opperator)
|
|
|
|
|
// We need to check those for specific functions
|
|
|
|
|
if expr, ok := n.(*ast.SelectorExpr); ok { |
|
|
|
|
x, ok := expr.X.(*ast.Ident) |
|
|
|
|
if !ok { |
|
|
|
|
// in this case we are deep in an access
|
|
|
|
|
// example, fmt is banned, but pouet isn't.
|
|
|
|
|
// we must allow pouet.fmt.x but not fmt.x
|
|
|
|
|
// this is the pouet.fmt.x case.
|
|
|
|
|
return c |
|
|
|
|
} |
|
|
|
|
pkg := allowedImp[x.Name] |
|
|
|
|
f := x.Name + "." + expr.Sel.Name |
|
|
|
|
if funcDeclPkg[f] != nil { |
|
|
|
|
c.Calls = append(c.Calls, f) |
|
|
|
|
return c |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if pkg == nil { |
|
|
|
|
if allImports[x.Name] { |
|
|
|
|
pos := c.getPos(n) |
|
|
|
|
setIllegal("illegal-access", f, pos) |
|
|
|
|
} |
|
|
|
|
return c |
|
|
|
|
} |
|
|
|
|
if !pkg["*"] && !pkg[expr.Sel.Name] { |
|
|
|
|
// all the package is not whiteList and is not explicitly allowed
|
|
|
|
|
pos := c.getPos(n) |
|
|
|
|
setIllegal("illegal-access", f, pos) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ex, ok := n.(*ast.ArrayType); ok { |
|
|
|
|
if op, ok := ex.Elt.(*ast.Ident); ok { |
|
|
|
|
arraysInstances = append(arraysInstances, nodePos{ |
|
|
|
|
name: op.Name, |
|
|
|
|
position: c.getPos(n), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if _, ok := n.(*ast.ForStmt); ok { |
|
|
|
|
forStmts = append(forStmts, nodePos{ |
|
|
|
|
name: "for", |
|
|
|
|
position: c.getPos(n), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return c |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (fv *fileVisitor) checkImport(n ast.Node) ast.Visitor { |
|
|
|
|
if spec, ok := n.(*ast.ImportSpec); ok { |
|
|
|
|
pkg := spec.Path.Value[1 : len(spec.Path.Value)-1] |
|
|
|
|
if allowedImp[pkg] == nil { |
|
|
|
|
pos := fv.getPos(n) |
|
|
|
|
setIllegal("illegal-import", pkg, pos) |
|
|
|
|
return fv |
|
|
|
|
} |
|
|
|
|
// if the import is named, we need to move it to the new name
|
|
|
|
|
name := "" |
|
|
|
|
if spec.Name != nil { |
|
|
|
|
name = spec.Name.Name |
|
|
|
|
} else if strings.ContainsRune(pkg, '/') { |
|
|
|
|
parts := strings.Split(pkg, "/") |
|
|
|
|
name = parts[len(parts)-1] |
|
|
|
|
} |
|
|
|
|
if allowedImp[pkg] != nil { |
|
|
|
|
if name != "" { |
|
|
|
|
allowedImp[name] = allowedImp[pkg] |
|
|
|
|
allowedImp[pkg] = nil |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if isRelativeImport(pkg) { |
|
|
|
|
if importPkg == nil { |
|
|
|
|
importPkg = make(map[string]*pkgFunc) |
|
|
|
|
} |
|
|
|
|
if name != "" { |
|
|
|
|
importPkg[name] = &pkgFunc{ |
|
|
|
|
path: pkg, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
relativeImports = append(relativeImports, name) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return fv |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (i *impVisitor) Visit(n ast.Node) ast.Visitor { |
|
|
|
|
if spec, ok := n.(*ast.ImportSpec); ok { |
|
|
|
|
pkg := spec.Path.Value[1 : len(spec.Path.Value)-1] |
|
|
|
|
if allImports == nil { |
|
|
|
|
allImports = make(map[string]bool) |
|
|
|
|
} |
|
|
|
|
pkgSplit := strings.Split(pkg, "/") |
|
|
|
|
|
|
|
|
|
allImports[pkg] = true |
|
|
|
|
allImports[pkgSplit[len(pkgSplit)-1]] = true |
|
|
|
|
|
|
|
|
|
if isRelativeImport(pkg) { |
|
|
|
|
i.relativeImports = append(i.relativeImports, pkg) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return i |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func funcs(s *ast.Scope) []string { |
|
|
|
|
scopeStr := s.String() |
|
|
|
|
scp := strings.Split(scopeStr, "\n") |
|
|
|
|
var funcs []string |
|
|
|
|
for _, v := range scp { |
|
|
|
|
nv := strings.TrimSpace(v) |
|
|
|
|
if strings.HasPrefix(nv, "func ") { |
|
|
|
|
funcs = append(funcs, strings.TrimPrefix(nv, "func ")) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return funcs |
|
|
|
|
} |
|
|
|
|