├── README.md ├── analyzer.go ├── data.yaml ├── go.mod ├── go.sum ├── jxss.php ├── main.go ├── namespace_resolver.go ├── test.php └── traverser.go /README.md: -------------------------------------------------------------------------------- 1 | # php-analyzer 2 | Current performance: 3 | `2022/05/28 12:41:35 Scanned 4981 files Found 428 vulns In time 25.159522618s` 4 | 5 | Input is filenames or URLs of PHP files. 6 | 7 | Output is the PHP representation of the vertex of the assignment or sink and it's line:char position, along with the traced stack for each step in the path. 8 | 9 | To do: 10 | - HTML context awareness 11 | - Don't traverse tree multiple times (and dont traverse dead code) 12 | - Scan whole repos instead of files (this will require more WP research) 13 | 14 | Example: 15 | ``` 16 | $ echo test.php | php-analyzer -yaml 17 | file: test.php 18 | type: xss 19 | path: 20 | - stack: '[assign] $user_input <- [taint] $_GET' 21 | code: $user_input = $_GET['input'] 11:194 22 | - stack: '[assign] $improperly_filtered <- [filter] MAGICQUOTES <- [taint] $user_input' 23 | code: $improperly_filtered = "$user_input" 12:224 24 | - stack: '[sink] echo <- [taint] $improperly_filtered' 25 | code: |- 26 | // this does alert because magic quotes dont stop xss 27 | echo $improperly_filtered; 20:444 28 | 29 | file: test.php 30 | type: sqli 31 | path: 32 | - stack: '[assign] $t <- [assign] $param <- [taint] $_GET' 33 | code: $d->dangerous($_GET) 14:282 34 | - stack: '[assign] $temp <- [filter] unknown_filter_func <- [taint] $param' 35 | code: $temp = unknown_filter_func($param) 4:51 36 | - stack: '[assign] dangerous <- [taint] $temp' 37 | code: return $temp; 7:174 38 | - stack: '[assign] $t <- [taint] dangerous' 39 | code: $t = $d->dangerous($_GET) 14:277 40 | - stack: '[sink] query <- [taint] $t' 41 | code: |- 42 | // alerts because taint follows through method call into $t 43 | query($t) 23:532 44 | 45 | 2022/06/01 18:42:23 Scanned 1 files Found 2 vulns In time 4.785042ms 46 | ``` 47 | 48 | Help: 49 | ``` 50 | $ ./php-analyzer -h 51 | Usage of ./php-analyzer: 52 | -d int 53 | Number of times to traverse the tree (Tracing through function calls requires multiple passes) (default 10) 54 | -f string 55 | Specify a data file of sources, sinks, and filters (default "data.yaml") 56 | -t int 57 | Number of goroutines to use (default 100) 58 | -yaml 59 | Output as YAML, (JSON by default) 60 | ``` 61 | -------------------------------------------------------------------------------- /analyzer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/VKCOM/php-parser/pkg/ast" 8 | "github.com/VKCOM/php-parser/pkg/visitor" 9 | "gopkg.in/yaml.v2" 10 | ) 11 | 12 | type Context struct { 13 | Class string 14 | Block string 15 | } 16 | 17 | type Vuln struct { 18 | Sources []string 19 | Sinks []string 20 | Args map[string][]int 21 | Filters []string 22 | } 23 | 24 | type Taint struct { 25 | Name string 26 | Type string 27 | Scope Context 28 | Vertex ast.Vertex 29 | Parent *Taint 30 | Stack string 31 | } 32 | 33 | type Item struct { 34 | Name string 35 | Type string 36 | Scope Context 37 | Vertex ast.Vertex 38 | } 39 | 40 | type Analyzer struct { 41 | visitor.Null 42 | 43 | CallStack []Item 44 | Tainted []Taint 45 | CurrentContext Context 46 | Data map[string]Vuln 47 | Filename string 48 | } 49 | 50 | func NewAnalyzer(filename string, datafile string) *Analyzer { 51 | var analyzer = &Analyzer{ 52 | Filename: filename, 53 | } 54 | 55 | analyzer.LoadData(datafile) 56 | 57 | return analyzer 58 | } 59 | 60 | func (a *Analyzer) Push(item Item) { 61 | a.CallStack = append([]Item{item}, a.CallStack...) 62 | } 63 | 64 | func (a *Analyzer) Pop() Item { 65 | ret := a.CallStack[0] 66 | a.CallStack = a.CallStack[1:] 67 | return ret 68 | } 69 | 70 | func (a *Analyzer) Top() Item { 71 | return a.CallStack[0] 72 | } 73 | 74 | func (a *Analyzer) DumpStack(taint Taint) string { 75 | str := "" 76 | for i := len(a.CallStack) - 1; i >= 0; i-- { 77 | str += "[" + a.CallStack[i].Type + "] " + a.CallStack[i].Name + " <- " 78 | } 79 | str += "[taint] " + taint.Name 80 | return str 81 | } 82 | 83 | // Trace up to the nearest sink, assignment, or valid filter 84 | func (a *Analyzer) Trace(taint Taint) { 85 | for _, item := range a.CallStack { 86 | switch item.Type { 87 | case "filter": 88 | for _, f := range a.Data[taint.Type].Filters { 89 | if item.Name == f { 90 | return 91 | } 92 | } 93 | case "sink": 94 | for sink, _ := range a.Data[taint.Type].Args { 95 | if item.Name == sink { 96 | // send to results when a taint meets a sink 97 | Results <- Result{Vertex: item.Vertex, Type: taint.Type, LastTaint: taint, Filename: a.Filename, Stack: a.DumpStack(taint)} 98 | } 99 | } 100 | for _, sink := range a.Data[taint.Type].Sinks { 101 | if item.Name == sink { 102 | // send to results when a taint meets a sink 103 | Results <- Result{Vertex: item.Vertex, Type: taint.Type, LastTaint: taint, Filename: a.Filename, Stack: a.DumpStack(taint)} 104 | } 105 | } 106 | case "assign": 107 | a.AddTaint(Taint{Name: item.Name, Type: taint.Type, Scope: item.Scope, Vertex: item.Vertex, Parent: &taint, Stack: a.DumpStack(taint)}) 108 | return 109 | case "break": 110 | return 111 | } 112 | } 113 | } 114 | 115 | func (a *Analyzer) VarVertex(name string) { 116 | for _, taint := range a.Tainted { 117 | if taint.Name == name { 118 | if a.CompareContexts(taint.Scope, a.CurrentContext) { 119 | a.Trace(taint) 120 | } 121 | } 122 | } 123 | } 124 | 125 | // search for taints to track 126 | 127 | func (a *Analyzer) ExprVariable(n *ast.ExprVariable) { 128 | id, ok := n.Name.(*ast.Identifier) 129 | if !ok { 130 | return 131 | } 132 | name := string(id.Value) 133 | 134 | a.VarVertex(name) 135 | } 136 | 137 | func (a *Analyzer) ExprPropertyFetch(n *ast.ExprPropertyFetch) { 138 | id, ok := n.Prop.(*ast.Identifier) 139 | if !ok { 140 | return 141 | } 142 | name := string(id.Value) 143 | 144 | a.VarVertex(name) 145 | } 146 | 147 | func (a *Analyzer) ExprFunctionCall(n *ast.ExprFunctionCall) { 148 | name := "" 149 | funcName, ok := n.Function.(*ast.Name) 150 | if !ok { 151 | return 152 | } 153 | for _, v := range funcName.Parts { 154 | name += string(v.(*ast.NamePart).Value) 155 | } 156 | 157 | a.VarVertex(name) 158 | } 159 | 160 | func (a *Analyzer) ExprMethodCall(n *ast.ExprMethodCall) { 161 | obj, ok := n.Var.(*ast.ExprVariable) 162 | if !ok { 163 | return 164 | } 165 | cid, ok := obj.Name.(*ast.Identifier) 166 | if !ok { 167 | return 168 | } 169 | mid, ok := n.Method.(*ast.Identifier) 170 | if !ok { 171 | return 172 | } 173 | 174 | methodname := string(mid.Value) 175 | fullname := string(cid.Value) + "->" + methodname 176 | 177 | a.VarVertex(fullname) 178 | a.VarVertex(methodname) 179 | } 180 | 181 | // auxiliary funcs 182 | 183 | func (a *Analyzer) AddTaint(add Taint) { 184 | for _, taint := range a.Tainted { 185 | if a.CompareTaints(add, taint) { 186 | return 187 | } 188 | } 189 | 190 | //log.Println("Tainted: ", add.Name, add.Type, &add.Scope) 191 | a.Tainted = append(a.Tainted, add) 192 | } 193 | 194 | func (a *Analyzer) CompareTaints(t1 Taint, t2 Taint) bool { 195 | return t1.Name == t2.Name && t1.Type == t2.Type && a.CompareContexts(t1.Scope, t2.Scope) 196 | } 197 | 198 | func (a *Analyzer) CompareContexts(c1 Context, c2 Context) bool { 199 | return (c1.Block == c2.Block || c1.Block == "*" || c2.Block == "*") && (c1.Class == c2.Class || c1.Class == "*" || c2.Class == "*") 200 | } 201 | 202 | func (a *Analyzer) LoadData(filename string) { 203 | file, err := os.Open(filename) 204 | if err != nil { 205 | log.Fatal(err) 206 | } 207 | d := yaml.NewDecoder(file) 208 | 209 | err = d.Decode(&a.Data) 210 | if err != nil { 211 | panic(err) 212 | } 213 | 214 | // add sources to taint list 215 | for t, vuln := range a.Data { 216 | for _, source := range vuln.Sources { 217 | a.AddTaint(Taint{Name: source, Type: t, Scope: Context{Class: "*", Block: "*"}}) 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /data.yaml: -------------------------------------------------------------------------------- 1 | csrf: 2 | sources: 3 | - "$_POST" 4 | - "$_SERVER" 5 | - "$_REQUEST" 6 | - "$_COOKIE" 7 | sinks: 8 | - "echo" 9 | - "print" 10 | - "print_r" 11 | - "exit" 12 | - "die" 13 | filters: 14 | - "json_encode" 15 | - "wp_hash_password" 16 | - "empty" 17 | - "htmlspecialchars" 18 | - "htmlentities" 19 | - "highlight_string" 20 | - "urlencode" 21 | - "(int)" 22 | - "(bool)" 23 | - "(double)" 24 | - "unset" 25 | - "intval" 26 | - "absint" 27 | xss: 28 | sources: 29 | - "$_GET" 30 | args: 31 | sinks: 32 | - "echo" 33 | - "print" 34 | - "print_r" 35 | - "exit" 36 | - "die" 37 | - "printf" 38 | filters: 39 | - "wp_hash_password" 40 | - "json_encode" 41 | - "empty" 42 | - "htmlspecialchars" 43 | - "htmlentities" 44 | - "highlight_string" 45 | - "urlencode" 46 | - "(int)" 47 | - "(bool)" 48 | - "(double)" 49 | - "unset" 50 | - "intval" 51 | - "absint" 52 | sqli: 53 | sources: 54 | - "$_GET" 55 | - "$_POST" 56 | - "$_SERVER" 57 | - "$_REQUEST" 58 | - "$_COOKIE" 59 | - "$_SESSION" 60 | - "$_FILES" 61 | - "$php_errormsg" 62 | args: 63 | sinks: 64 | - "querySingle" 65 | - "real_query" 66 | - "maxdb_real_query" 67 | - "maxdb_query" 68 | - "ingres_prepare" 69 | - "cubrid_query" 70 | - "cubrid_execute" 71 | - "sqlite_open" 72 | - "sqlite_popen" 73 | - "$wpdb->get_col" 74 | - "$wpdb->get_results" 75 | - "$wpdb->replace" 76 | - "dbx_query" 77 | - "msql_db_query" 78 | filters: 79 | - "wp_hash_password" 80 | - "MAGICQUOTES" 81 | - "json_encode" 82 | - "empty" 83 | - "htmlspecialchars" 84 | - "absint" 85 | - "$wpdb->prepare" 86 | - "(int)" 87 | - "(bool)" 88 | - "(double)" 89 | - "unset" 90 | - "intval" 91 | - "sanitize_text_field" 92 | - "esc_sql" 93 | - "$db->escape" 94 | - "escapeString" 95 | lfi: 96 | sources: 97 | - "$_GET" 98 | - "$_POST" 99 | - "$_SERVER" 100 | - "$_REQUEST" 101 | - "$_COOKIE" 102 | - "$_SESSION" 103 | - "$_FILES" 104 | - "$php_errormsg" 105 | args: 106 | sinks: 107 | - "include" 108 | - "include_once" 109 | - "require" 110 | - "require_once" 111 | #- "fread" 112 | filters: 113 | - "wp_hash_password" 114 | - "json_encode" 115 | - "empty" 116 | - "(int)" 117 | - "intval" 118 | - "absint" 119 | - "(bool)" 120 | - "(double)" 121 | - "unset" 122 | lfd: 123 | sources: 124 | - "$_GET" 125 | - "$_SESSION" 126 | - "$_FILES" 127 | - "$_COOKIE" 128 | - "$_POST" 129 | sinks: 130 | - "bzread" 131 | - "bzflush" 132 | - "dio_read" 133 | - "eio_readdir" 134 | - "fdf_open" 135 | - "file" 136 | - "file_get_contents" 137 | - "finfo_file" 138 | - "fflush" 139 | - "fgetc" 140 | - "fgetcsv" 141 | - "fgets" 142 | - "fgetss" 143 | - "fread" 144 | - "fpassthru" 145 | - "fscanf" 146 | - "zip_open" 147 | filters: 148 | - "wp_hash_password" 149 | - "json_encode" 150 | - "empty" 151 | - "(int)" 152 | - "intval" 153 | - "absint" 154 | - "(bool)" 155 | - "(double)" 156 | - "unset" 157 | rce: 158 | sources: 159 | - "$_GET" 160 | - "$_POST" 161 | - "$_SERVER" 162 | - "$_REQUEST" 163 | - "$_COOKIE" 164 | - "$_SESSION" 165 | - "$_FILES" 166 | - "$php_errormsg" 167 | args: 168 | "preg_replace": 169 | - 1 170 | sinks: 171 | - "shell_exec" 172 | - "exec" 173 | - "assert" 174 | - "passthru" 175 | - "popen" 176 | - "create_function" 177 | - "proc_open" 178 | - "pcntl_exec" 179 | - "system" 180 | - "eval" 181 | - "php://input" 182 | - "call_user_func" 183 | filters: 184 | - "wp_hash_password" 185 | - "json_encode" 186 | - "empty" 187 | - "(int)" 188 | - "(bool)" 189 | - "(double)" 190 | - "unset" 191 | - "intval" 192 | - "escapeshellcmd" 193 | - "escapeshellarg" 194 | - "addslashes" 195 | - "mysql_real_escape_string" 196 | - "escapeString" 197 | - "absint" 198 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module php-analyzer 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/VKCOM/noverify v0.5.2 7 | github.com/VKCOM/php-parser v0.8.0-rc.2.0.20210802093708-d85f5a481602 8 | gopkg.in/yaml.v2 v2.4.0 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/VKCOM/noverify v0.5.2 h1:7FHtI6s9Wojk5qb2/jYhhiDXtd8RfpRR7b0ccE91fr8= 2 | github.com/VKCOM/noverify v0.5.2/go.mod h1:4YqmJFxSi6Hw7kBiUZ7GzeQgqHLyxEopsSVhIf9/htA= 3 | github.com/VKCOM/php-parser v0.8.0-rc.2.0.20210802093708-d85f5a481602 h1:FYasGDmh13Lr5zwqIkxoYZsr/YlEIMTIpqzIu/syeYY= 4 | github.com/VKCOM/php-parser v0.8.0-rc.2.0.20210802093708-d85f5a481602/go.mod h1:wLtaD4M5K8bJPwjkl4BVone8dbMiG1OApbMKdjubCEw= 5 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 6 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 7 | github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= 8 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 9 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= 10 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 11 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 12 | github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= 13 | github.com/quasilyte/regex/syntax v0.0.0-20200419152657-af9db7f4a3ab/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= 14 | github.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Ya9AIoYBpE= 15 | go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I= 16 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 17 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 19 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 20 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 21 | gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= 22 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 23 | -------------------------------------------------------------------------------- /jxss.php: -------------------------------------------------------------------------------- 1 | "; 40 | 41 | // if no article specified, show the list of articles 42 | if (!isset($_GET['article'])) { 43 | $articles = scandir("wp-content/"); 44 | $i = 0; 45 | foreach ($articles as $article) { 46 | if (str_ends_with($article, ".html")) { 47 | echo "" . $article . "
"; 48 | } 49 | } 50 | } else { 51 | // otherwise display article 52 | 53 | $filename = get_filename($content_dir); 54 | 55 | if (file_exists($filename)) { 56 | $sanitized_name = preg_replace('/(\.\.)/', '', $filename); 57 | $file = fopen( $sanitized_name, "r" ); 58 | if( $file == false ) { 59 | echo ( "Error in opening file" ); 60 | exit(); 61 | } 62 | $filesize = filesize( $sanitized_name ); 63 | $filetext = fread( $file, $filesize ); 64 | fclose( $file ); 65 | 66 | echo $filetext; 67 | } else { 68 | echo "The article you are looking for does not exist :("; 69 | } 70 | } 71 | echo ""; 72 | } 73 | 74 | function get_filename($dir) { 75 | $filename = $dir . $_GET['article']; 76 | return $filename; 77 | } 78 | 79 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/json" 7 | "flag" 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "log" 12 | "net/http" 13 | "os" 14 | "strings" 15 | "sync" 16 | "time" 17 | 18 | "github.com/VKCOM/noverify/src/php/parseutil" 19 | "github.com/VKCOM/php-parser/pkg/ast" 20 | "github.com/VKCOM/php-parser/pkg/visitor/printer" 21 | "gopkg.in/yaml.v2" 22 | ) 23 | 24 | var ( 25 | Queue = make(chan string) 26 | Results = make(chan Result) 27 | sm sync.Map 28 | Files = 0 29 | Vulns = 0 30 | ) 31 | 32 | type Result struct { 33 | Vertex ast.Vertex 34 | Type string 35 | Code string 36 | Stack string 37 | LastTaint Taint 38 | Filename string 39 | } 40 | 41 | func main() { 42 | depth := flag.Int("d", 10, "Number of times to traverse the tree (Tracing through function calls requires multiple passes)") 43 | threads := flag.Int("t", 100, "Number of goroutines to use") 44 | datafile := flag.String("f", "data.yaml", "Specify a data file of sources, sinks, and filters") 45 | fyaml := flag.Bool("yaml", false, "Output as YAML, (JSON by default)") 46 | flag.Parse() 47 | 48 | t := time.Now() 49 | 50 | defer func() { 51 | log.Printf("Scanned %d files\tFound %d vulns\tIn time %v", Files, Vulns, time.Since(t)) 52 | }() 53 | 54 | go reader() 55 | go workers(*depth, *threads, *datafile) 56 | writer(*fyaml) 57 | 58 | } 59 | 60 | func worker(depth int, datafile string) { 61 | 62 | // recover from parseutil.ParseFie() panic on bad syntax 63 | 64 | for filename := range Queue { 65 | defer func() { 66 | if err := recover(); err != nil { 67 | log.Println("RECOVERING:", err, "\tFILE:", filename) 68 | worker(depth, datafile) 69 | } 70 | }() 71 | // read the file 72 | content, err := readFile(filename) 73 | if err != nil { 74 | log.Println(err) 75 | continue 76 | } 77 | 78 | // convert PHP to AST 79 | root, err := parseutil.ParseFile(content) 80 | if err != nil { 81 | log.Println(err, filename) 82 | continue 83 | } 84 | 85 | Files++ 86 | 87 | // create visitor 88 | a := NewAnalyzer(filename, datafile) 89 | t := NewTraverser(a) 90 | for j := 0; j < depth; j++ { 91 | t.Traverse(root) 92 | } 93 | } 94 | } 95 | 96 | func workers(depth int, n int, datafile string) { 97 | var wg sync.WaitGroup 98 | for i := 0; i < n; i++ { 99 | wg.Add(1) 100 | go func() { 101 | defer wg.Done() 102 | 103 | worker(depth, datafile) 104 | }() 105 | } 106 | wg.Wait() 107 | close(Results) 108 | } 109 | 110 | func reader() { 111 | s := bufio.NewScanner(os.Stdin) 112 | for s.Scan() { 113 | filename := s.Text() 114 | 115 | Queue <- filename 116 | } 117 | close(Queue) 118 | } 119 | 120 | func writer(fyaml bool) { 121 | for result := range Results { 122 | defer func() { 123 | if err := recover(); err != nil { 124 | writer(fyaml) 125 | } 126 | }() 127 | 128 | type tt struct { 129 | Stack string 130 | Code string 131 | } 132 | var taintPath []tt 133 | var reversed []tt 134 | taint := result.LastTaint 135 | 136 | o := bytes.NewBufferString("") 137 | //f := formatter.NewFormatter().WithState(formatter.FormatterStatePHP) 138 | //result.Vertex.Accept(f) 139 | p := printer.NewPrinter(o).WithState(printer.PrinterStatePHP) 140 | result.Vertex.Accept(p) 141 | code := fmt.Sprintf("%s %d:%d", strings.TrimSpace(o.String()), result.Vertex.GetPosition().StartLine, result.Vertex.GetPosition().StartPos) 142 | 143 | type output struct { 144 | File string 145 | Type string 146 | Path []tt 147 | } 148 | 149 | taintPath = append(taintPath, tt{Code: code, Stack: result.Stack}) 150 | for taint.Vertex != nil { 151 | o := bytes.NewBufferString("") 152 | //d := dumper.NewDumper(o) 153 | p := printer.NewPrinter(o).WithState(printer.PrinterStatePHP) 154 | //result.Vertex.Accept(f) 155 | taint.Vertex.Accept(p) 156 | taintstring := fmt.Sprintf("%s %d:%d", strings.TrimSpace(o.String()), taint.Vertex.GetPosition().StartLine, taint.Vertex.GetPosition().StartPos) 157 | taintPath = append(taintPath, tt{Code: taintstring, Stack: taint.Stack}) 158 | 159 | taint = *taint.Parent 160 | } 161 | 162 | for i := len(taintPath) - 1; i >= 0; i-- { 163 | reversed = append(reversed, taintPath[i]) 164 | } 165 | 166 | var ( 167 | bytes []byte 168 | err error 169 | ) 170 | 171 | if !(fyaml) { 172 | bytes, err = json.Marshal(output{ 173 | File: result.Filename, 174 | Type: result.Type, 175 | Path: reversed, 176 | }) 177 | if err != nil { 178 | log.Println(err) 179 | } 180 | } else { 181 | bytes, err = yaml.Marshal(output{ 182 | File: result.Filename, 183 | Type: result.Type, 184 | Path: reversed, 185 | }) 186 | if err != nil { 187 | log.Println(err) 188 | } 189 | } 190 | 191 | if isUnique(code) { 192 | Vulns++ 193 | fmt.Println(string(bytes)) 194 | } 195 | } 196 | } 197 | 198 | func readFile(filename string) ([]byte, error) { 199 | if strings.HasPrefix(filename, "http://") || strings.HasPrefix(filename, "https://") { 200 | return download(filename) 201 | } else { 202 | return ioutil.ReadFile(filename) 203 | } 204 | } 205 | 206 | func isUnique(url string) bool { 207 | _, present := sm.Load(url) 208 | if present { 209 | return false 210 | } 211 | sm.Store(url, true) 212 | return true 213 | } 214 | 215 | func download(u string) ([]byte, error) { 216 | resp, err := http.Get(u) 217 | if err != nil { 218 | return nil, err 219 | } 220 | 221 | defer resp.Body.Close() 222 | 223 | bodyBytes, err := io.ReadAll(resp.Body) 224 | if err != nil { 225 | return nil, err 226 | } 227 | 228 | return bodyBytes, nil 229 | } 230 | -------------------------------------------------------------------------------- /namespace_resolver.go: -------------------------------------------------------------------------------- 1 | // Package visitor contains walker.visitor implementations 2 | package main 3 | 4 | import ( 5 | "errors" 6 | "strings" 7 | 8 | "github.com/VKCOM/php-parser/pkg/ast" 9 | "github.com/VKCOM/php-parser/pkg/visitor" 10 | ) 11 | 12 | // NamespaceResolver visitor 13 | type NamespaceResolver struct { 14 | visitor.Null 15 | Namespace *Namespace 16 | ResolvedNames map[ast.Vertex]string 17 | 18 | goDeep bool 19 | } 20 | 21 | // NewNamespaceResolver NamespaceResolver type constructor 22 | func NewNamespaceResolver() *NamespaceResolver { 23 | return &NamespaceResolver{ 24 | Namespace: NewNamespace(""), 25 | ResolvedNames: map[ast.Vertex]string{}, 26 | goDeep: true, 27 | } 28 | } 29 | 30 | func (nsr *NamespaceResolver) EnterNode(n ast.Vertex) bool { 31 | n.Accept(nsr) 32 | 33 | if !nsr.goDeep { 34 | nsr.goDeep = true 35 | return false 36 | } 37 | 38 | return true 39 | } 40 | 41 | func (nsr *NamespaceResolver) StmtFunction(n *ast.StmtFunction) { 42 | nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value)) 43 | 44 | for _, parameter := range n.Params { 45 | nsr.ResolveType(parameter.(*ast.Parameter).Type) 46 | } 47 | 48 | if n.ReturnType != nil { 49 | nsr.ResolveType(n.ReturnType) 50 | } 51 | } 52 | 53 | func (nsr *NamespaceResolver) StmtClassMethod(n *ast.StmtClassMethod) { 54 | nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value)) 55 | 56 | for _, parameter := range n.Params { 57 | nsr.ResolveType(parameter.(*ast.Parameter).Type) 58 | } 59 | 60 | if n.ReturnType != nil { 61 | nsr.ResolveType(n.ReturnType) 62 | } 63 | } 64 | 65 | // LeaveNode is invoked after node process 66 | func (nsr *NamespaceResolver) LeaveNode(n ast.Vertex) { 67 | switch nn := n.(type) { 68 | case *ast.StmtNamespace: 69 | if nn.Stmts != nil { 70 | nsr.Namespace = NewNamespace("") 71 | } 72 | } 73 | } 74 | 75 | // AddAlias adds a new alias 76 | func (nsr *NamespaceResolver) AddAlias(useType string, nn ast.Vertex, prefix []ast.Vertex) { 77 | switch use := nn.(type) { 78 | case *ast.StmtUse: 79 | if use.Type != nil { 80 | useType = string(use.Type.(*ast.Identifier).Value) 81 | } 82 | 83 | useNameParts := use.Use.(*ast.Name).Parts 84 | var alias string 85 | if use.Alias == nil { 86 | alias = string(useNameParts[len(useNameParts)-1].(*ast.NamePart).Value) 87 | } else { 88 | alias = string(use.Alias.(*ast.Identifier).Value) 89 | } 90 | 91 | nsr.Namespace.AddAlias(useType, concatNameParts(prefix, useNameParts), alias) 92 | } 93 | } 94 | 95 | // AddNamespacedName adds namespaced name by node 96 | func (nsr *NamespaceResolver) AddNamespacedName(nn ast.Vertex, nodeName string) { 97 | if nsr.Namespace.Namespace == "" { 98 | nsr.ResolvedNames[nn] = nodeName 99 | } else { 100 | nsr.ResolvedNames[nn] = nsr.Namespace.Namespace + "\\" + nodeName 101 | } 102 | } 103 | 104 | // ResolveName adds a resolved fully qualified name by node 105 | func (nsr *NamespaceResolver) ResolveName(nameNode ast.Vertex, aliasType string) { 106 | resolved, err := nsr.Namespace.ResolveName(nameNode, aliasType) 107 | if err == nil { 108 | nsr.ResolvedNames[nameNode] = resolved 109 | } 110 | } 111 | 112 | // ResolveType adds a resolved fully qualified type name 113 | func (nsr *NamespaceResolver) ResolveType(n ast.Vertex) { 114 | switch nn := n.(type) { 115 | case *ast.Nullable: 116 | nsr.ResolveType(nn.Expr) 117 | case *ast.Name: 118 | nsr.ResolveName(n, "") 119 | case *ast.NameRelative: 120 | nsr.ResolveName(n, "") 121 | case *ast.NameFullyQualified: 122 | nsr.ResolveName(n, "") 123 | } 124 | } 125 | 126 | // Namespace context 127 | type Namespace struct { 128 | Namespace string 129 | Aliases map[string]map[string]string 130 | } 131 | 132 | // NewNamespace constructor 133 | func NewNamespace(NSName string) *Namespace { 134 | return &Namespace{ 135 | Namespace: NSName, 136 | Aliases: map[string]map[string]string{ 137 | "": {}, 138 | "const": {}, 139 | "function": {}, 140 | }, 141 | } 142 | } 143 | 144 | // AddAlias adds a new alias 145 | func (ns *Namespace) AddAlias(aliasType string, aliasName string, alias string) { 146 | aliasType = strings.ToLower(aliasType) 147 | 148 | if aliasType == "const" { 149 | ns.Aliases[aliasType][alias] = aliasName 150 | } else { 151 | ns.Aliases[aliasType][strings.ToLower(alias)] = aliasName 152 | } 153 | } 154 | 155 | // ResolveName returns a resolved fully qualified name 156 | func (ns *Namespace) ResolveName(nameNode ast.Vertex, aliasType string) (string, error) { 157 | switch n := nameNode.(type) { 158 | case *ast.NameFullyQualified: 159 | // Fully qualifid name is already resolved 160 | return concatNameParts(n.Parts), nil 161 | 162 | case *ast.NameRelative: 163 | if ns.Namespace == "" { 164 | return concatNameParts(n.Parts), nil 165 | } 166 | return ns.Namespace + "\\" + concatNameParts(n.Parts), nil 167 | 168 | case *ast.Name: 169 | if aliasType == "const" && len(n.Parts) == 1 { 170 | part := strings.ToLower(string(n.Parts[0].(*ast.NamePart).Value)) 171 | if part == "true" || part == "false" || part == "null" { 172 | return part, nil 173 | } 174 | } 175 | 176 | if aliasType == "" && len(n.Parts) == 1 { 177 | part := strings.ToLower(string(n.Parts[0].(*ast.NamePart).Value)) 178 | 179 | switch part { 180 | case "self": 181 | fallthrough 182 | case "static": 183 | fallthrough 184 | case "parent": 185 | fallthrough 186 | case "int": 187 | fallthrough 188 | case "float": 189 | fallthrough 190 | case "bool": 191 | fallthrough 192 | case "string": 193 | fallthrough 194 | case "void": 195 | fallthrough 196 | case "iterable": 197 | fallthrough 198 | case "object": 199 | return part, nil 200 | } 201 | } 202 | 203 | aliasName, err := ns.ResolveAlias(nameNode, aliasType) 204 | if err != nil { 205 | // resolve as relative name if alias not found 206 | if ns.Namespace == "" { 207 | return concatNameParts(n.Parts), nil 208 | } 209 | return ns.Namespace + "\\" + concatNameParts(n.Parts), nil 210 | } 211 | 212 | if len(n.Parts) > 1 { 213 | // if name qualified, replace first part by alias 214 | return aliasName + "\\" + concatNameParts(n.Parts[1:]), nil 215 | } 216 | 217 | return aliasName, nil 218 | } 219 | 220 | return "", errors.New("must be instance of name.Names") 221 | } 222 | 223 | // ResolveAlias returns alias or error if not found 224 | func (ns *Namespace) ResolveAlias(nameNode ast.Vertex, aliasType string) (string, error) { 225 | aliasType = strings.ToLower(aliasType) 226 | nameParts := nameNode.(*ast.Name).Parts 227 | 228 | firstPartStr := string(nameParts[0].(*ast.NamePart).Value) 229 | 230 | if len(nameParts) > 1 { // resolve aliases for qualified names, always against class alias type 231 | firstPartStr = strings.ToLower(firstPartStr) 232 | aliasType = "" 233 | } else { 234 | if aliasType != "const" { // constants are case-sensitive 235 | firstPartStr = strings.ToLower(firstPartStr) 236 | } 237 | } 238 | 239 | aliasName, ok := ns.Aliases[aliasType][firstPartStr] 240 | if !ok { 241 | return "", errors.New("Not found") 242 | } 243 | 244 | return aliasName, nil 245 | } 246 | 247 | func concatNameParts(parts ...[]ast.Vertex) string { 248 | str := "" 249 | 250 | for _, p := range parts { 251 | for _, n := range p { 252 | if str == "" { 253 | str = string(n.(*ast.NamePart).Value) 254 | } else { 255 | str = str + "\\" + string(n.(*ast.NamePart).Value) 256 | } 257 | } 258 | } 259 | 260 | return str 261 | } 262 | -------------------------------------------------------------------------------- /test.php: -------------------------------------------------------------------------------- 1 | dangerous($_GET); 15 | 16 | // this does not alert because the input was sanitized 17 | query($improperly_filtered); 18 | 19 | // this does alert because magic quotes dont stop xss 20 | echo $improperly_filtered; 21 | 22 | // alerts because taint follows through method call into $t 23 | query($t); 24 | ?> 25 | -------------------------------------------------------------------------------- /traverser.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/VKCOM/php-parser/pkg/ast" 5 | "github.com/VKCOM/php-parser/pkg/visitor/traverser" 6 | ) 7 | 8 | type Traverser struct { 9 | v *Analyzer 10 | ResolvedNames map[string]ast.Vertex 11 | } 12 | 13 | func NewTraverser(v *Analyzer) *Traverser { 14 | ret := &Traverser{ 15 | v: v, 16 | } 17 | return ret 18 | } 19 | 20 | func (t *Traverser) ResolveNames(n ast.Vertex) { 21 | res := NewNamespaceResolver() 22 | traverser := traverser.NewTraverser(res) 23 | n.Accept(traverser) 24 | 25 | t.ResolvedNames = make(map[string]ast.Vertex) 26 | for vert, str := range res.ResolvedNames { 27 | t.ResolvedNames[str] = vert 28 | } 29 | } 30 | 31 | func (t *Traverser) Traverse(n ast.Vertex) { 32 | if n != nil { 33 | n.Accept(t) 34 | } 35 | } 36 | 37 | func (t *Traverser) Root(n *ast.Root) { 38 | t.ResolveNames(n) 39 | n.Accept(t.v) 40 | 41 | for _, nn := range n.Stmts { 42 | nn.Accept(t) 43 | } 44 | } 45 | 46 | func (t *Traverser) Nullable(n *ast.Nullable) { 47 | n.Accept(t.v) 48 | 49 | t.Traverse(n.Expr) 50 | } 51 | 52 | func (t *Traverser) Parameter(n *ast.Parameter) { 53 | n.Accept(t.v) 54 | 55 | for _, nn := range n.AttrGroups { 56 | nn.Accept(t) 57 | } 58 | 59 | for _, nn := range n.Modifiers { 60 | nn.Accept(t) 61 | } 62 | 63 | t.Traverse(n.Type) 64 | t.Traverse(n.Var) 65 | t.Traverse(n.DefaultValue) 66 | } 67 | 68 | func (t *Traverser) Identifier(n *ast.Identifier) { 69 | n.Accept(t.v) 70 | } 71 | 72 | func (t *Traverser) Argument(n *ast.Argument) { 73 | n.Accept(t.v) 74 | 75 | t.Traverse(n.Name) 76 | t.Traverse(n.Expr) 77 | } 78 | 79 | func (t *Traverser) MatchArm(n *ast.MatchArm) { 80 | n.Accept(t.v) 81 | 82 | for _, nn := range n.Exprs { 83 | nn.Accept(t) 84 | } 85 | t.Traverse(n.ReturnExpr) 86 | } 87 | 88 | func (t *Traverser) Union(n *ast.Union) { 89 | n.Accept(t.v) 90 | 91 | for _, nn := range n.Types { 92 | nn.Accept(t) 93 | } 94 | } 95 | 96 | func (t *Traverser) Attribute(n *ast.Attribute) { 97 | n.Accept(t.v) 98 | 99 | t.Traverse(n.Name) 100 | for _, nn := range n.Args { 101 | nn.Accept(t) 102 | } 103 | } 104 | 105 | func (t *Traverser) AttributeGroup(n *ast.AttributeGroup) { 106 | n.Accept(t.v) 107 | 108 | for _, nn := range n.Attrs { 109 | nn.Accept(t) 110 | } 111 | } 112 | 113 | func (t *Traverser) StmtBreak(n *ast.StmtBreak) { 114 | n.Accept(t.v) 115 | 116 | t.Traverse(n.Expr) 117 | } 118 | 119 | func (t *Traverser) StmtCase(n *ast.StmtCase) { 120 | n.Accept(t.v) 121 | 122 | t.v.Push(Item{Name: "condition", Type: "break", Vertex: n}) 123 | t.Traverse(n.Cond) 124 | _ = t.v.Pop() 125 | for _, nn := range n.Stmts { 126 | nn.Accept(t) 127 | } 128 | } 129 | 130 | func (t *Traverser) StmtCatch(n *ast.StmtCatch) { 131 | n.Accept(t.v) 132 | 133 | for _, nn := range n.Types { 134 | nn.Accept(t) 135 | } 136 | t.Traverse(n.Var) 137 | for _, nn := range n.Stmts { 138 | nn.Accept(t) 139 | } 140 | } 141 | 142 | func (t *Traverser) StmtEnum(n *ast.StmtEnum) { 143 | n.Accept(t.v) 144 | 145 | for _, nn := range n.AttrGroups { 146 | nn.Accept(t) 147 | } 148 | t.Traverse(n.Name) 149 | t.Traverse(n.Type) 150 | 151 | for _, nn := range n.Implements { 152 | nn.Accept(t) 153 | } 154 | for _, nn := range n.Stmts { 155 | nn.Accept(t) 156 | } 157 | } 158 | 159 | func (t *Traverser) EnumCase(n *ast.EnumCase) { 160 | n.Accept(t.v) 161 | 162 | for _, nn := range n.AttrGroups { 163 | nn.Accept(t) 164 | } 165 | t.Traverse(n.Name) 166 | t.Traverse(n.Expr) 167 | } 168 | 169 | func (t *Traverser) StmtClass(n *ast.StmtClass) { 170 | name, ok := n.Name.(*ast.Identifier) 171 | if ok { 172 | t.v.CurrentContext.Class = string(name.Value) 173 | defer func() { t.v.CurrentContext.Class = "" }() 174 | } 175 | 176 | n.Accept(t.v) 177 | 178 | for _, nn := range n.AttrGroups { 179 | nn.Accept(t) 180 | } 181 | for _, nn := range n.Modifiers { 182 | nn.Accept(t) 183 | } 184 | t.Traverse(n.Name) 185 | for _, nn := range n.Args { 186 | nn.Accept(t) 187 | } 188 | t.Traverse(n.Extends) 189 | for _, nn := range n.Implements { 190 | nn.Accept(t) 191 | } 192 | for _, nn := range n.Stmts { 193 | nn.Accept(t) 194 | } 195 | } 196 | 197 | func (t *Traverser) StmtClassConstList(n *ast.StmtClassConstList) { 198 | n.Accept(t.v) 199 | 200 | for _, nn := range n.AttrGroups { 201 | nn.Accept(t) 202 | } 203 | for _, nn := range n.Modifiers { 204 | nn.Accept(t) 205 | } 206 | for _, nn := range n.Consts { 207 | nn.Accept(t) 208 | } 209 | } 210 | 211 | func (t *Traverser) StmtClassMethod(n *ast.StmtClassMethod) { 212 | name, ok := n.Name.(*ast.Identifier) 213 | if ok { 214 | t.v.CurrentContext.Block = string(name.Value) 215 | defer func() { t.v.CurrentContext.Block = "" }() 216 | } 217 | 218 | n.Accept(t.v) 219 | 220 | for _, nn := range n.AttrGroups { 221 | nn.Accept(t) 222 | } 223 | for _, nn := range n.Modifiers { 224 | nn.Accept(t) 225 | } 226 | t.Traverse(n.Name) 227 | for _, nn := range n.Params { 228 | nn.Accept(t) 229 | } 230 | t.Traverse(n.ReturnType) 231 | t.Traverse(n.Stmt) 232 | } 233 | 234 | func (t *Traverser) StmtConstList(n *ast.StmtConstList) { 235 | n.Accept(t.v) 236 | 237 | for _, nn := range n.Consts { 238 | nn.Accept(t) 239 | } 240 | } 241 | 242 | func (t *Traverser) StmtConstant(n *ast.StmtConstant) { 243 | n.Accept(t.v) 244 | 245 | t.Traverse(n.Name) 246 | t.Traverse(n.Expr) 247 | } 248 | 249 | func (t *Traverser) StmtContinue(n *ast.StmtContinue) { 250 | n.Accept(t.v) 251 | 252 | t.Traverse(n.Expr) 253 | } 254 | 255 | func (t *Traverser) StmtDeclare(n *ast.StmtDeclare) { 256 | n.Accept(t.v) 257 | 258 | for _, nn := range n.Consts { 259 | nn.Accept(t) 260 | } 261 | t.Traverse(n.Stmt) 262 | } 263 | 264 | func (t *Traverser) StmtDefault(n *ast.StmtDefault) { 265 | n.Accept(t.v) 266 | 267 | for _, nn := range n.Stmts { 268 | nn.Accept(t) 269 | } 270 | } 271 | 272 | func (t *Traverser) StmtDo(n *ast.StmtDo) { 273 | n.Accept(t.v) 274 | 275 | t.Traverse(n.Stmt) 276 | 277 | t.v.Push(Item{Name: "condition", Type: "break", Vertex: n}) 278 | t.Traverse(n.Cond) 279 | _ = t.v.Pop() 280 | } 281 | 282 | func (t *Traverser) StmtEcho(n *ast.StmtEcho) { 283 | t.v.Push(Item{Name: "echo", Type: "sink", Vertex: n}) 284 | defer t.v.Pop() 285 | n.Accept(t.v) 286 | 287 | for _, nn := range n.Exprs { 288 | nn.Accept(t) 289 | } 290 | } 291 | 292 | func (t *Traverser) StmtElse(n *ast.StmtElse) { 293 | n.Accept(t.v) 294 | 295 | t.Traverse(n.Stmt) 296 | } 297 | 298 | func (t *Traverser) StmtElseIf(n *ast.StmtElseIf) { 299 | n.Accept(t.v) 300 | 301 | t.v.Push(Item{Name: "condition", Type: "break", Vertex: n}) 302 | t.Traverse(n.Cond) 303 | _ = t.v.Pop() 304 | t.Traverse(n.Stmt) 305 | } 306 | 307 | func (t *Traverser) StmtExpression(n *ast.StmtExpression) { 308 | n.Accept(t.v) 309 | 310 | t.Traverse(n.Expr) 311 | } 312 | 313 | func (t *Traverser) StmtFinally(n *ast.StmtFinally) { 314 | n.Accept(t.v) 315 | 316 | for _, nn := range n.Stmts { 317 | nn.Accept(t) 318 | } 319 | } 320 | 321 | func (t *Traverser) StmtFor(n *ast.StmtFor) { 322 | n.Accept(t.v) 323 | 324 | for _, nn := range n.Init { 325 | nn.Accept(t) 326 | } 327 | t.v.Push(Item{Name: "condition", Type: "break", Vertex: n}) 328 | for _, nn := range n.Cond { 329 | nn.Accept(t) 330 | } 331 | _ = t.v.Pop() 332 | for _, nn := range n.Loop { 333 | nn.Accept(t) 334 | } 335 | t.Traverse(n.Stmt) 336 | } 337 | 338 | func (t *Traverser) StmtForeach(n *ast.StmtForeach) { 339 | n.Accept(t.v) 340 | 341 | t.Traverse(n.Expr) 342 | t.Traverse(n.Key) 343 | t.Traverse(n.Var) 344 | t.Traverse(n.Stmt) 345 | } 346 | 347 | func (t *Traverser) StmtFunction(n *ast.StmtFunction) { 348 | name, ok := n.Name.(*ast.Identifier) 349 | if ok { 350 | t.v.CurrentContext.Block = string(name.Value) 351 | defer func() { t.v.CurrentContext.Block = "" }() 352 | } 353 | 354 | n.Accept(t.v) 355 | 356 | for _, nn := range n.AttrGroups { 357 | nn.Accept(t) 358 | } 359 | t.Traverse(n.Name) 360 | for _, nn := range n.Params { 361 | nn.Accept(t) 362 | } 363 | t.Traverse(n.ReturnType) 364 | for _, nn := range n.Stmts { 365 | nn.Accept(t) 366 | } 367 | } 368 | 369 | func (t *Traverser) StmtGlobal(n *ast.StmtGlobal) { 370 | n.Accept(t.v) 371 | 372 | for _, nn := range n.Vars { 373 | nn.Accept(t) 374 | } 375 | } 376 | 377 | func (t *Traverser) StmtGoto(n *ast.StmtGoto) { 378 | n.Accept(t.v) 379 | 380 | t.Traverse(n.Label) 381 | } 382 | 383 | func (t *Traverser) StmtHaltCompiler(n *ast.StmtHaltCompiler) { 384 | n.Accept(t.v) 385 | } 386 | 387 | func (t *Traverser) StmtIf(n *ast.StmtIf) { 388 | n.Accept(t.v) 389 | 390 | t.v.Push(Item{Name: "condition", Type: "break", Vertex: n}) 391 | t.Traverse(n.Cond) 392 | _ = t.v.Pop() 393 | t.Traverse(n.Stmt) 394 | for _, nn := range n.ElseIf { 395 | nn.Accept(t) 396 | } 397 | t.Traverse(n.Else) 398 | } 399 | 400 | func (t *Traverser) StmtInlineHtml(n *ast.StmtInlineHtml) { 401 | n.Accept(t.v) 402 | } 403 | 404 | func (t *Traverser) StmtInterface(n *ast.StmtInterface) { 405 | n.Accept(t.v) 406 | 407 | for _, nn := range n.AttrGroups { 408 | nn.Accept(t) 409 | } 410 | t.Traverse(n.Name) 411 | for _, nn := range n.Extends { 412 | nn.Accept(t) 413 | } 414 | for _, nn := range n.Stmts { 415 | nn.Accept(t) 416 | } 417 | } 418 | 419 | func (t *Traverser) StmtLabel(n *ast.StmtLabel) { 420 | n.Accept(t.v) 421 | 422 | t.Traverse(n.Name) 423 | } 424 | 425 | func (t *Traverser) StmtNamespace(n *ast.StmtNamespace) { 426 | n.Accept(t.v) 427 | 428 | t.Traverse(n.Name) 429 | for _, nn := range n.Stmts { 430 | nn.Accept(t) 431 | } 432 | } 433 | 434 | func (t *Traverser) StmtNop(n *ast.StmtNop) { 435 | n.Accept(t.v) 436 | } 437 | 438 | func (t *Traverser) StmtProperty(n *ast.StmtProperty) { 439 | n.Accept(t.v) 440 | 441 | t.Traverse(n.Var) 442 | t.Traverse(n.Expr) 443 | } 444 | 445 | func (t *Traverser) StmtPropertyList(n *ast.StmtPropertyList) { 446 | n.Accept(t.v) 447 | 448 | for _, nn := range n.AttrGroups { 449 | nn.Accept(t) 450 | } 451 | for _, nn := range n.Modifiers { 452 | nn.Accept(t) 453 | } 454 | t.Traverse(n.Type) 455 | for _, nn := range n.Props { 456 | nn.Accept(t) 457 | } 458 | } 459 | 460 | func (t *Traverser) StmtReturn(n *ast.StmtReturn) { 461 | t.v.Push(Item{Name: t.v.CurrentContext.Block, Type: "assign", Scope: Context{Class: "*", Block: "*"}, Vertex: n}) 462 | defer t.v.Pop() 463 | 464 | n.Accept(t.v) 465 | 466 | t.Traverse(n.Expr) 467 | } 468 | 469 | func (t *Traverser) StmtStatic(n *ast.StmtStatic) { 470 | n.Accept(t.v) 471 | 472 | for _, nn := range n.Vars { 473 | nn.Accept(t) 474 | } 475 | } 476 | 477 | func (t *Traverser) StmtStaticVar(n *ast.StmtStaticVar) { 478 | n.Accept(t.v) 479 | 480 | t.Traverse(n.Var) 481 | t.Traverse(n.Expr) 482 | } 483 | 484 | func (t *Traverser) StmtStmtList(n *ast.StmtStmtList) { 485 | n.Accept(t.v) 486 | 487 | for _, nn := range n.Stmts { 488 | nn.Accept(t) 489 | } 490 | } 491 | 492 | func (t *Traverser) StmtSwitch(n *ast.StmtSwitch) { 493 | n.Accept(t.v) 494 | 495 | t.v.Push(Item{Name: "condition", Type: "break", Vertex: n}) 496 | t.Traverse(n.Cond) 497 | _ = t.v.Pop() 498 | for _, nn := range n.Cases { 499 | nn.Accept(t) 500 | } 501 | } 502 | 503 | func (t *Traverser) StmtThrow(n *ast.StmtThrow) { 504 | n.Accept(t.v) 505 | 506 | t.Traverse(n.Expr) 507 | } 508 | 509 | func (t *Traverser) StmtTrait(n *ast.StmtTrait) { 510 | n.Accept(t.v) 511 | 512 | for _, nn := range n.AttrGroups { 513 | nn.Accept(t) 514 | } 515 | t.Traverse(n.Name) 516 | for _, nn := range n.Stmts { 517 | nn.Accept(t) 518 | } 519 | } 520 | 521 | func (t *Traverser) StmtTraitUse(n *ast.StmtTraitUse) { 522 | n.Accept(t.v) 523 | 524 | for _, nn := range n.Traits { 525 | nn.Accept(t) 526 | } 527 | for _, nn := range n.Adaptations { 528 | nn.Accept(t) 529 | } 530 | } 531 | 532 | func (t *Traverser) StmtTraitUseAlias(n *ast.StmtTraitUseAlias) { 533 | n.Accept(t.v) 534 | 535 | t.Traverse(n.Trait) 536 | t.Traverse(n.Method) 537 | t.Traverse(n.Modifier) 538 | t.Traverse(n.Alias) 539 | } 540 | 541 | func (t *Traverser) StmtTraitUsePrecedence(n *ast.StmtTraitUsePrecedence) { 542 | n.Accept(t.v) 543 | 544 | t.Traverse(n.Trait) 545 | t.Traverse(n.Method) 546 | for _, nn := range n.Insteadof { 547 | nn.Accept(t) 548 | } 549 | } 550 | 551 | func (t *Traverser) StmtTry(n *ast.StmtTry) { 552 | n.Accept(t.v) 553 | 554 | for _, nn := range n.Stmts { 555 | nn.Accept(t) 556 | } 557 | for _, nn := range n.Catches { 558 | nn.Accept(t) 559 | } 560 | t.Traverse(n.Finally) 561 | } 562 | 563 | func (t *Traverser) StmtUnset(n *ast.StmtUnset) { 564 | n.Accept(t.v) 565 | 566 | for _, nn := range n.Vars { 567 | nn.Accept(t) 568 | } 569 | } 570 | 571 | func (t *Traverser) StmtUse(n *ast.StmtUseList) { 572 | n.Accept(t.v) 573 | 574 | t.Traverse(n.Type) 575 | for _, nn := range n.Uses { 576 | nn.Accept(t) 577 | } 578 | } 579 | 580 | func (t *Traverser) StmtGroupUse(n *ast.StmtGroupUseList) { 581 | n.Accept(t.v) 582 | 583 | t.Traverse(n.Type) 584 | t.Traverse(n.Prefix) 585 | for _, nn := range n.Uses { 586 | nn.Accept(t) 587 | } 588 | } 589 | 590 | func (t *Traverser) StmtUseDeclaration(n *ast.StmtUse) { 591 | n.Accept(t.v) 592 | 593 | t.Traverse(n.Type) 594 | t.Traverse(n.Use) 595 | t.Traverse(n.Alias) 596 | } 597 | 598 | func (t *Traverser) StmtWhile(n *ast.StmtWhile) { 599 | n.Accept(t.v) 600 | 601 | t.v.Push(Item{Name: "condition", Type: "break", Vertex: n}) 602 | t.Traverse(n.Cond) 603 | _ = t.v.Pop() 604 | t.Traverse(n.Stmt) 605 | } 606 | 607 | func (t *Traverser) ExprArray(n *ast.ExprArray) { 608 | n.Accept(t.v) 609 | 610 | for _, nn := range n.Items { 611 | nn.Accept(t) 612 | } 613 | } 614 | 615 | func (t *Traverser) ExprArrayDimFetch(n *ast.ExprArrayDimFetch) { 616 | dim, ok := n.Dim.(*ast.ScalarString) 617 | if ok { 618 | variable, ok := n.Var.(*ast.ExprVariable) 619 | if ok { 620 | id, ok := variable.Name.(*ast.Identifier) 621 | if ok { 622 | // filter $_GET['page'] 623 | if string(id.Value) == "$_GET" { 624 | if string(dim.Value) == "'page'" || string(dim.Value) == "\"page\"" { 625 | return 626 | } 627 | } 628 | } 629 | } 630 | } 631 | 632 | n.Accept(t.v) 633 | 634 | t.Traverse(n.Var) 635 | t.Traverse(n.Dim) 636 | } 637 | 638 | func (t *Traverser) ExprArrayItem(n *ast.ExprArrayItem) { 639 | n.Accept(t.v) 640 | 641 | t.Traverse(n.Key) 642 | t.Traverse(n.Val) 643 | } 644 | 645 | func (t *Traverser) ExprArrowFunction(n *ast.ExprArrowFunction) { 646 | n.Accept(t.v) 647 | 648 | for _, nn := range n.AttrGroups { 649 | nn.Accept(t) 650 | } 651 | for _, nn := range n.Params { 652 | nn.Accept(t) 653 | } 654 | t.Traverse(n.ReturnType) 655 | t.Traverse(n.Expr) 656 | } 657 | 658 | func (t *Traverser) ExprBitwiseNot(n *ast.ExprBitwiseNot) { 659 | n.Accept(t.v) 660 | 661 | t.Traverse(n.Expr) 662 | } 663 | 664 | func (t *Traverser) ExprBooleanNot(n *ast.ExprBooleanNot) { 665 | n.Accept(t.v) 666 | 667 | t.Traverse(n.Expr) 668 | } 669 | 670 | func (t *Traverser) ExprBrackets(n *ast.ExprBrackets) { 671 | n.Accept(t.v) 672 | 673 | t.Traverse(n.Expr) 674 | } 675 | 676 | func (t *Traverser) ExprClassConstFetch(n *ast.ExprClassConstFetch) { 677 | n.Accept(t.v) 678 | 679 | t.Traverse(n.Class) 680 | t.Traverse(n.Const) 681 | } 682 | 683 | func (t *Traverser) ExprClone(n *ast.ExprClone) { 684 | n.Accept(t.v) 685 | 686 | t.Traverse(n.Expr) 687 | } 688 | 689 | func (t *Traverser) ExprClosure(n *ast.ExprClosure) { 690 | n.Accept(t.v) 691 | 692 | for _, nn := range n.AttrGroups { 693 | nn.Accept(t) 694 | } 695 | for _, nn := range n.Params { 696 | nn.Accept(t) 697 | } 698 | for _, nn := range n.Uses { 699 | nn.Accept(t) 700 | } 701 | t.Traverse(n.ReturnType) 702 | for _, nn := range n.Stmts { 703 | nn.Accept(t) 704 | } 705 | } 706 | 707 | func (t *Traverser) ExprClosureUse(n *ast.ExprClosureUse) { 708 | n.Accept(t.v) 709 | 710 | t.Traverse(n.Var) 711 | } 712 | 713 | func (t *Traverser) ExprConstFetch(n *ast.ExprConstFetch) { 714 | n.Accept(t.v) 715 | 716 | t.Traverse(n.Const) 717 | } 718 | 719 | func (t *Traverser) ExprEmpty(n *ast.ExprEmpty) { 720 | n.Accept(t.v) 721 | 722 | t.Traverse(n.Expr) 723 | } 724 | 725 | func (t *Traverser) ExprErrorSuppress(n *ast.ExprErrorSuppress) { 726 | n.Accept(t.v) 727 | 728 | t.Traverse(n.Expr) 729 | } 730 | 731 | func (t *Traverser) ExprEval(n *ast.ExprEval) { 732 | n.Accept(t.v) 733 | 734 | t.Traverse(n.Expr) 735 | } 736 | 737 | func (t *Traverser) ExprExit(n *ast.ExprExit) { 738 | t.v.Push(Item{Name: string(n.ExitTkn.Value), Type: "sink", Vertex: n}) 739 | defer t.v.Pop() 740 | 741 | n.Accept(t.v) 742 | 743 | t.Traverse(n.Expr) 744 | } 745 | 746 | func (t *Traverser) ExprFunctionCall(n *ast.ExprFunctionCall) { 747 | // build function name 748 | name := "" 749 | funcName, ok := n.Function.(*ast.Name) 750 | if !ok { 751 | return 752 | } 753 | for _, v := range funcName.Parts { 754 | name += string(v.(*ast.NamePart).Value) 755 | } 756 | 757 | var args []int 758 | callType := "filter" 759 | 760 | for _, vuln := range t.v.Data { 761 | for _, sink := range vuln.Sinks { 762 | if name == sink { 763 | callType = "sink" 764 | } 765 | } 766 | } 767 | 768 | for _, vuln := range t.v.Data { 769 | for str, arg := range vuln.Args { 770 | if name == str { 771 | callType = "argument" 772 | args = arg 773 | } 774 | } 775 | } 776 | 777 | vert, ok := t.ResolvedNames[name] 778 | if ok { 779 | callType = "custom" 780 | } 781 | 782 | switch callType { 783 | case "filter": 784 | t.v.Push(Item{Name: name, Type: "filter", Vertex: n}) 785 | 786 | n.Accept(t.v) 787 | 788 | t.Traverse(n.Function) 789 | for _, nn := range n.Args { 790 | nn.Accept(t) 791 | } 792 | 793 | _ = t.v.Pop() 794 | case "sink": 795 | t.v.Push(Item{Name: name, Type: "sink", Vertex: n}) 796 | 797 | n.Accept(t.v) 798 | 799 | t.Traverse(n.Function) 800 | 801 | for _, nn := range n.Args { 802 | nn.Accept(t) 803 | } 804 | 805 | _ = t.v.Pop() 806 | case "custom": 807 | n.Accept(t.v) 808 | 809 | t.Traverse(n.Function) 810 | 811 | for i, nn := range n.Args { 812 | function, ok := vert.(*ast.StmtFunction) 813 | if !ok { 814 | nn.Accept(t) 815 | continue 816 | } 817 | if len(function.Params) <= i { 818 | nn.Accept(t) 819 | continue 820 | } 821 | param, ok := function.Params[i].(*ast.Parameter) 822 | if !ok { 823 | nn.Accept(t) 824 | continue 825 | } 826 | variable, ok := param.Var.(*ast.ExprVariable) 827 | if !ok { 828 | nn.Accept(t) 829 | continue 830 | } 831 | id, ok := variable.Name.(*ast.Identifier) 832 | if !ok { 833 | nn.Accept(t) 834 | continue 835 | } 836 | 837 | t.v.Push(Item{Name: string(id.Value), Type: "assign", Scope: Context{Class: "*", Block: name}, Vertex: n}) 838 | 839 | nn.Accept(t) 840 | 841 | _ = t.v.Pop() 842 | } 843 | 844 | case "argument": 845 | n.Accept(t.v) 846 | 847 | t.Traverse(n.Function) 848 | 849 | for i, nn := range n.Args { 850 | for _, j := range args { 851 | if i == j { 852 | // sink arg 853 | t.v.Push(Item{Name: name, Type: "sink", Vertex: n}) 854 | 855 | nn.Accept(t) 856 | 857 | _ = t.v.Pop() 858 | } else { 859 | nn.Accept(t) 860 | } 861 | } 862 | } 863 | } 864 | } 865 | 866 | func (t *Traverser) ExprInclude(n *ast.ExprInclude) { 867 | n.Accept(t.v) 868 | 869 | t.Traverse(n.Expr) 870 | } 871 | 872 | func (t *Traverser) ExprIncludeOnce(n *ast.ExprIncludeOnce) { 873 | n.Accept(t.v) 874 | 875 | t.Traverse(n.Expr) 876 | } 877 | 878 | func (t *Traverser) ExprInstanceOf(n *ast.ExprInstanceOf) { 879 | n.Accept(t.v) 880 | 881 | t.Traverse(n.Expr) 882 | t.Traverse(n.Class) 883 | } 884 | 885 | func (t *Traverser) ExprIsset(n *ast.ExprIsset) { 886 | t.v.Push(Item{Name: "isset", Type: "break", Vertex: n}) 887 | defer t.v.Pop() 888 | 889 | n.Accept(t.v) 890 | 891 | for _, nn := range n.Vars { 892 | nn.Accept(t) 893 | } 894 | } 895 | 896 | func (t *Traverser) ExprList(n *ast.ExprList) { 897 | n.Accept(t.v) 898 | 899 | for _, nn := range n.Items { 900 | nn.Accept(t) 901 | } 902 | } 903 | 904 | func (t *Traverser) ExprMethodCall(n *ast.ExprMethodCall) { 905 | id, ok := n.Method.(*ast.Identifier) 906 | if !ok { 907 | return 908 | } 909 | 910 | name := string(id.Value) 911 | 912 | var args []int 913 | callType := "filter" 914 | 915 | for _, vuln := range t.v.Data { 916 | for _, sink := range vuln.Sinks { 917 | if name == sink { 918 | callType = "sink" 919 | } 920 | } 921 | } 922 | 923 | for _, vuln := range t.v.Data { 924 | for str, arg := range vuln.Args { 925 | if name == str { 926 | callType = "argument" 927 | args = arg 928 | } 929 | } 930 | } 931 | 932 | vert, ok := t.ResolvedNames[name] 933 | if ok { 934 | callType = "custom" 935 | } 936 | 937 | switch callType { 938 | case "filter": 939 | t.v.Push(Item{Name: name, Type: "filter", Vertex: n}) 940 | 941 | n.Accept(t.v) 942 | 943 | t.Traverse(n.Var) 944 | t.Traverse(n.Method) 945 | for _, nn := range n.Args { 946 | nn.Accept(t) 947 | } 948 | 949 | _ = t.v.Pop() 950 | case "sink": 951 | t.v.Push(Item{Name: name, Type: "sink", Vertex: n}) 952 | 953 | n.Accept(t.v) 954 | 955 | t.Traverse(n.Var) 956 | t.Traverse(n.Method) 957 | for _, nn := range n.Args { 958 | nn.Accept(t) 959 | } 960 | 961 | _ = t.v.Pop() 962 | case "custom": 963 | n.Accept(t.v) 964 | 965 | t.Traverse(n.Var) 966 | t.Traverse(n.Method) 967 | for i, nn := range n.Args { 968 | function, ok := vert.(*ast.StmtClassMethod) 969 | if !ok { 970 | nn.Accept(t) 971 | continue 972 | } 973 | if len(function.Params) <= i { 974 | nn.Accept(t) 975 | continue 976 | } 977 | param, ok := function.Params[i].(*ast.Parameter) 978 | if !ok { 979 | nn.Accept(t) 980 | continue 981 | } 982 | variable, ok := param.Var.(*ast.ExprVariable) 983 | if !ok { 984 | nn.Accept(t) 985 | continue 986 | } 987 | id, ok := variable.Name.(*ast.Identifier) 988 | if !ok { 989 | nn.Accept(t) 990 | continue 991 | } 992 | 993 | t.v.Push(Item{Name: string(id.Value), Type: "assign", Scope: Context{Class: "*", Block: name}, Vertex: n}) 994 | 995 | nn.Accept(t) 996 | 997 | _ = t.v.Pop() 998 | } 999 | 1000 | case "argument": 1001 | n.Accept(t.v) 1002 | 1003 | t.Traverse(n.Method) 1004 | t.Traverse(n.Var) 1005 | 1006 | for i, nn := range n.Args { 1007 | for _, j := range args { 1008 | if i == j { 1009 | // sink arg 1010 | t.v.Push(Item{Name: name, Type: "sink", Vertex: n}) 1011 | 1012 | nn.Accept(t) 1013 | 1014 | _ = t.v.Pop() 1015 | } else { 1016 | nn.Accept(t) 1017 | } 1018 | } 1019 | } 1020 | } 1021 | } 1022 | 1023 | func (t *Traverser) ExprNullsafeMethodCall(n *ast.ExprNullsafeMethodCall) { 1024 | n.Accept(t.v) 1025 | 1026 | t.Traverse(n.Var) 1027 | t.Traverse(n.Method) 1028 | for _, nn := range n.Args { 1029 | nn.Accept(t) 1030 | } 1031 | } 1032 | 1033 | func (t *Traverser) ExprNew(n *ast.ExprNew) { 1034 | n.Accept(t.v) 1035 | 1036 | t.Traverse(n.Class) 1037 | for _, nn := range n.Args { 1038 | nn.Accept(t) 1039 | } 1040 | } 1041 | 1042 | func (t *Traverser) ExprPostDec(n *ast.ExprPostDec) { 1043 | n.Accept(t.v) 1044 | 1045 | t.Traverse(n.Var) 1046 | } 1047 | 1048 | func (t *Traverser) ExprPostInc(n *ast.ExprPostInc) { 1049 | n.Accept(t.v) 1050 | 1051 | t.Traverse(n.Var) 1052 | } 1053 | 1054 | func (t *Traverser) ExprPreDec(n *ast.ExprPreDec) { 1055 | n.Accept(t.v) 1056 | 1057 | t.Traverse(n.Var) 1058 | } 1059 | 1060 | func (t *Traverser) ExprPreInc(n *ast.ExprPreInc) { 1061 | n.Accept(t.v) 1062 | 1063 | t.Traverse(n.Var) 1064 | } 1065 | 1066 | func (t *Traverser) ExprPrint(n *ast.ExprPrint) { 1067 | t.v.Push(Item{Name: "print", Type: "sink", Vertex: n}) 1068 | defer t.v.Pop() 1069 | n.Accept(t.v) 1070 | 1071 | t.Traverse(n.Expr) 1072 | } 1073 | 1074 | func (t *Traverser) ExprPropertyFetch(n *ast.ExprPropertyFetch) { 1075 | n.Accept(t.v) 1076 | 1077 | t.Traverse(n.Var) 1078 | t.Traverse(n.Prop) 1079 | } 1080 | 1081 | func (t *Traverser) ExprNullsafePropertyFetch(n *ast.ExprNullsafePropertyFetch) { 1082 | n.Accept(t.v) 1083 | 1084 | t.Traverse(n.Var) 1085 | t.Traverse(n.Prop) 1086 | } 1087 | 1088 | func (t *Traverser) ExprRequire(n *ast.ExprRequire) { 1089 | n.Accept(t.v) 1090 | 1091 | t.Traverse(n.Expr) 1092 | } 1093 | 1094 | func (t *Traverser) ExprRequireOnce(n *ast.ExprRequireOnce) { 1095 | n.Accept(t.v) 1096 | 1097 | t.Traverse(n.Expr) 1098 | } 1099 | 1100 | func (t *Traverser) ExprShellExec(n *ast.ExprShellExec) { 1101 | t.v.Push(Item{Name: "shell_exec", Type: "sink", Vertex: n}) 1102 | defer t.v.Pop() 1103 | n.Accept(t.v) 1104 | 1105 | for _, nn := range n.Parts { 1106 | nn.Accept(t) 1107 | } 1108 | } 1109 | 1110 | func (t *Traverser) ExprStaticCall(n *ast.ExprStaticCall) { 1111 | n.Accept(t.v) 1112 | 1113 | t.Traverse(n.Class) 1114 | t.Traverse(n.Call) 1115 | for _, nn := range n.Args { 1116 | nn.Accept(t) 1117 | } 1118 | } 1119 | 1120 | func (t *Traverser) ExprStaticPropertyFetch(n *ast.ExprStaticPropertyFetch) { 1121 | n.Accept(t.v) 1122 | 1123 | t.Traverse(n.Class) 1124 | t.Traverse(n.Prop) 1125 | } 1126 | 1127 | func (t *Traverser) ExprTernary(n *ast.ExprTernary) { 1128 | n.Accept(t.v) 1129 | 1130 | t.v.Push(Item{Name: "condition", Type: "break", Vertex: n}) 1131 | t.Traverse(n.Cond) 1132 | _ = t.v.Pop() 1133 | 1134 | t.Traverse(n.IfTrue) 1135 | t.Traverse(n.IfFalse) 1136 | } 1137 | 1138 | func (t *Traverser) ExprUnaryMinus(n *ast.ExprUnaryMinus) { 1139 | n.Accept(t.v) 1140 | 1141 | t.Traverse(n.Expr) 1142 | } 1143 | 1144 | func (t *Traverser) ExprUnaryPlus(n *ast.ExprUnaryPlus) { 1145 | n.Accept(t.v) 1146 | 1147 | t.Traverse(n.Expr) 1148 | } 1149 | 1150 | func (t *Traverser) ExprVariable(n *ast.ExprVariable) { 1151 | n.Accept(t.v) 1152 | 1153 | t.Traverse(n.Name) 1154 | } 1155 | 1156 | func (t *Traverser) ExprYield(n *ast.ExprYield) { 1157 | n.Accept(t.v) 1158 | 1159 | t.Traverse(n.Key) 1160 | t.Traverse(n.Val) 1161 | } 1162 | 1163 | func (t *Traverser) ExprYieldFrom(n *ast.ExprYieldFrom) { 1164 | n.Accept(t.v) 1165 | 1166 | t.Traverse(n.Expr) 1167 | } 1168 | 1169 | func (t *Traverser) ExprAssign(n *ast.ExprAssign) { 1170 | switch variable := n.Var.(type) { 1171 | case *ast.ExprVariable: 1172 | name, ok := variable.Name.(*ast.Identifier) 1173 | if !ok { 1174 | break 1175 | } 1176 | 1177 | t.v.Push(Item{Name: string(name.Value), Type: "assign", Scope: t.v.CurrentContext, Vertex: n}) 1178 | defer t.v.Pop() 1179 | case *ast.ExprPropertyFetch: 1180 | name, ok := variable.Prop.(*ast.Identifier) 1181 | if !ok { 1182 | break 1183 | } 1184 | 1185 | t.v.Push(Item{Name: string(name.Value), Type: "assign", Scope: Context{Block: "*", Class: t.v.CurrentContext.Class}, Vertex: n}) 1186 | defer t.v.Pop() 1187 | } 1188 | 1189 | n.Accept(t.v) 1190 | 1191 | t.Traverse(n.Var) 1192 | t.Traverse(n.Expr) 1193 | } 1194 | 1195 | func (t *Traverser) ExprAssignReference(n *ast.ExprAssignReference) { 1196 | n.Accept(t.v) 1197 | 1198 | t.Traverse(n.Var) 1199 | t.Traverse(n.Expr) 1200 | } 1201 | 1202 | func (t *Traverser) ExprAssignBitwiseAnd(n *ast.ExprAssignBitwiseAnd) { 1203 | n.Accept(t.v) 1204 | 1205 | t.Traverse(n.Var) 1206 | t.Traverse(n.Expr) 1207 | } 1208 | 1209 | func (t *Traverser) ExprAssignBitwiseOr(n *ast.ExprAssignBitwiseOr) { 1210 | n.Accept(t.v) 1211 | 1212 | t.Traverse(n.Var) 1213 | t.Traverse(n.Expr) 1214 | } 1215 | 1216 | func (t *Traverser) ExprAssignBitwiseXor(n *ast.ExprAssignBitwiseXor) { 1217 | n.Accept(t.v) 1218 | 1219 | t.Traverse(n.Var) 1220 | t.Traverse(n.Expr) 1221 | } 1222 | 1223 | func (t *Traverser) ExprAssignCoalesce(n *ast.ExprAssignCoalesce) { 1224 | n.Accept(t.v) 1225 | 1226 | t.Traverse(n.Var) 1227 | t.Traverse(n.Expr) 1228 | } 1229 | 1230 | func (t *Traverser) ExprAssignConcat(n *ast.ExprAssignConcat) { 1231 | n.Accept(t.v) 1232 | 1233 | t.Traverse(n.Var) 1234 | t.Traverse(n.Expr) 1235 | } 1236 | 1237 | func (t *Traverser) ExprAssignDiv(n *ast.ExprAssignDiv) { 1238 | n.Accept(t.v) 1239 | 1240 | t.Traverse(n.Var) 1241 | t.Traverse(n.Expr) 1242 | } 1243 | 1244 | func (t *Traverser) ExprAssignMinus(n *ast.ExprAssignMinus) { 1245 | n.Accept(t.v) 1246 | 1247 | t.Traverse(n.Var) 1248 | t.Traverse(n.Expr) 1249 | } 1250 | 1251 | func (t *Traverser) ExprAssignMod(n *ast.ExprAssignMod) { 1252 | n.Accept(t.v) 1253 | 1254 | t.Traverse(n.Var) 1255 | t.Traverse(n.Expr) 1256 | } 1257 | 1258 | func (t *Traverser) ExprAssignMul(n *ast.ExprAssignMul) { 1259 | n.Accept(t.v) 1260 | 1261 | t.Traverse(n.Var) 1262 | t.Traverse(n.Expr) 1263 | } 1264 | 1265 | func (t *Traverser) ExprAssignPlus(n *ast.ExprAssignPlus) { 1266 | n.Accept(t.v) 1267 | 1268 | t.Traverse(n.Var) 1269 | t.Traverse(n.Expr) 1270 | } 1271 | 1272 | func (t *Traverser) ExprAssignPow(n *ast.ExprAssignPow) { 1273 | n.Accept(t.v) 1274 | 1275 | t.Traverse(n.Var) 1276 | t.Traverse(n.Expr) 1277 | } 1278 | 1279 | func (t *Traverser) ExprAssignShiftLeft(n *ast.ExprAssignShiftLeft) { 1280 | n.Accept(t.v) 1281 | 1282 | t.Traverse(n.Var) 1283 | t.Traverse(n.Expr) 1284 | } 1285 | 1286 | func (t *Traverser) ExprAssignShiftRight(n *ast.ExprAssignShiftRight) { 1287 | n.Accept(t.v) 1288 | 1289 | t.Traverse(n.Var) 1290 | t.Traverse(n.Expr) 1291 | } 1292 | 1293 | func (t *Traverser) ExprBinaryBitwiseAnd(n *ast.ExprBinaryBitwiseAnd) { 1294 | n.Accept(t.v) 1295 | 1296 | t.Traverse(n.Left) 1297 | t.Traverse(n.Right) 1298 | } 1299 | 1300 | func (t *Traverser) ExprBinaryBitwiseOr(n *ast.ExprBinaryBitwiseOr) { 1301 | n.Accept(t.v) 1302 | 1303 | t.Traverse(n.Left) 1304 | t.Traverse(n.Right) 1305 | } 1306 | 1307 | func (t *Traverser) ExprBinaryBitwiseXor(n *ast.ExprBinaryBitwiseXor) { 1308 | n.Accept(t.v) 1309 | 1310 | t.Traverse(n.Left) 1311 | t.Traverse(n.Right) 1312 | } 1313 | 1314 | func (t *Traverser) ExprBinaryBooleanAnd(n *ast.ExprBinaryBooleanAnd) { 1315 | n.Accept(t.v) 1316 | 1317 | t.Traverse(n.Left) 1318 | t.Traverse(n.Right) 1319 | } 1320 | 1321 | func (t *Traverser) ExprBinaryBooleanOr(n *ast.ExprBinaryBooleanOr) { 1322 | n.Accept(t.v) 1323 | 1324 | t.Traverse(n.Left) 1325 | t.Traverse(n.Right) 1326 | } 1327 | 1328 | func (t *Traverser) ExprBinaryCoalesce(n *ast.ExprBinaryCoalesce) { 1329 | n.Accept(t.v) 1330 | 1331 | t.Traverse(n.Left) 1332 | t.Traverse(n.Right) 1333 | } 1334 | 1335 | func (t *Traverser) ExprBinaryConcat(n *ast.ExprBinaryConcat) { 1336 | n.Accept(t.v) 1337 | 1338 | t.Traverse(n.Left) 1339 | t.Traverse(n.Right) 1340 | } 1341 | 1342 | func (t *Traverser) ExprBinaryDiv(n *ast.ExprBinaryDiv) { 1343 | n.Accept(t.v) 1344 | 1345 | t.Traverse(n.Left) 1346 | t.Traverse(n.Right) 1347 | } 1348 | 1349 | func (t *Traverser) ExprBinaryEqual(n *ast.ExprBinaryEqual) { 1350 | n.Accept(t.v) 1351 | 1352 | t.Traverse(n.Left) 1353 | t.Traverse(n.Right) 1354 | } 1355 | 1356 | func (t *Traverser) ExprBinaryGreater(n *ast.ExprBinaryGreater) { 1357 | n.Accept(t.v) 1358 | 1359 | t.Traverse(n.Left) 1360 | t.Traverse(n.Right) 1361 | } 1362 | 1363 | func (t *Traverser) ExprBinaryGreaterOrEqual(n *ast.ExprBinaryGreaterOrEqual) { 1364 | n.Accept(t.v) 1365 | 1366 | t.Traverse(n.Left) 1367 | t.Traverse(n.Right) 1368 | } 1369 | 1370 | func (t *Traverser) ExprBinaryIdentical(n *ast.ExprBinaryIdentical) { 1371 | n.Accept(t.v) 1372 | 1373 | t.Traverse(n.Left) 1374 | t.Traverse(n.Right) 1375 | } 1376 | 1377 | func (t *Traverser) ExprBinaryLogicalAnd(n *ast.ExprBinaryLogicalAnd) { 1378 | n.Accept(t.v) 1379 | 1380 | t.Traverse(n.Left) 1381 | t.Traverse(n.Right) 1382 | } 1383 | 1384 | func (t *Traverser) ExprBinaryLogicalOr(n *ast.ExprBinaryLogicalOr) { 1385 | n.Accept(t.v) 1386 | 1387 | t.Traverse(n.Left) 1388 | t.Traverse(n.Right) 1389 | } 1390 | 1391 | func (t *Traverser) ExprBinaryLogicalXor(n *ast.ExprBinaryLogicalXor) { 1392 | n.Accept(t.v) 1393 | 1394 | t.Traverse(n.Left) 1395 | t.Traverse(n.Right) 1396 | } 1397 | 1398 | func (t *Traverser) ExprBinaryMinus(n *ast.ExprBinaryMinus) { 1399 | n.Accept(t.v) 1400 | 1401 | t.Traverse(n.Left) 1402 | t.Traverse(n.Right) 1403 | } 1404 | 1405 | func (t *Traverser) ExprBinaryMod(n *ast.ExprBinaryMod) { 1406 | n.Accept(t.v) 1407 | 1408 | t.Traverse(n.Left) 1409 | t.Traverse(n.Right) 1410 | } 1411 | 1412 | func (t *Traverser) ExprBinaryMul(n *ast.ExprBinaryMul) { 1413 | n.Accept(t.v) 1414 | 1415 | t.Traverse(n.Left) 1416 | t.Traverse(n.Right) 1417 | } 1418 | 1419 | func (t *Traverser) ExprBinaryNotEqual(n *ast.ExprBinaryNotEqual) { 1420 | n.Accept(t.v) 1421 | 1422 | t.Traverse(n.Left) 1423 | t.Traverse(n.Right) 1424 | } 1425 | 1426 | func (t *Traverser) ExprBinaryNotIdentical(n *ast.ExprBinaryNotIdentical) { 1427 | n.Accept(t.v) 1428 | 1429 | t.Traverse(n.Left) 1430 | t.Traverse(n.Right) 1431 | } 1432 | 1433 | func (t *Traverser) ExprBinaryPlus(n *ast.ExprBinaryPlus) { 1434 | n.Accept(t.v) 1435 | 1436 | t.Traverse(n.Left) 1437 | t.Traverse(n.Right) 1438 | } 1439 | 1440 | func (t *Traverser) ExprBinaryPow(n *ast.ExprBinaryPow) { 1441 | n.Accept(t.v) 1442 | 1443 | t.Traverse(n.Left) 1444 | t.Traverse(n.Right) 1445 | } 1446 | 1447 | func (t *Traverser) ExprBinaryShiftLeft(n *ast.ExprBinaryShiftLeft) { 1448 | n.Accept(t.v) 1449 | 1450 | t.Traverse(n.Left) 1451 | t.Traverse(n.Right) 1452 | } 1453 | 1454 | func (t *Traverser) ExprBinaryShiftRight(n *ast.ExprBinaryShiftRight) { 1455 | n.Accept(t.v) 1456 | 1457 | t.Traverse(n.Left) 1458 | t.Traverse(n.Right) 1459 | } 1460 | 1461 | func (t *Traverser) ExprBinarySmaller(n *ast.ExprBinarySmaller) { 1462 | n.Accept(t.v) 1463 | 1464 | t.Traverse(n.Left) 1465 | t.Traverse(n.Right) 1466 | } 1467 | 1468 | func (t *Traverser) ExprBinarySmallerOrEqual(n *ast.ExprBinarySmallerOrEqual) { 1469 | n.Accept(t.v) 1470 | 1471 | t.Traverse(n.Left) 1472 | t.Traverse(n.Right) 1473 | } 1474 | 1475 | func (t *Traverser) ExprBinarySpaceship(n *ast.ExprBinarySpaceship) { 1476 | n.Accept(t.v) 1477 | 1478 | t.Traverse(n.Left) 1479 | t.Traverse(n.Right) 1480 | } 1481 | 1482 | func (t *Traverser) ExprCastArray(n *ast.ExprCastArray) { 1483 | n.Accept(t.v) 1484 | 1485 | t.Traverse(n.Expr) 1486 | } 1487 | 1488 | func (t *Traverser) ExprCastBool(n *ast.ExprCastBool) { 1489 | t.v.Push(Item{Name: "(bool)", Type: "break", Vertex: n}) 1490 | defer t.v.Pop() 1491 | 1492 | n.Accept(t.v) 1493 | 1494 | t.Traverse(n.Expr) 1495 | } 1496 | 1497 | func (t *Traverser) ExprCastDouble(n *ast.ExprCastDouble) { 1498 | t.v.Push(Item{Name: "(double)", Type: "break", Vertex: n}) 1499 | defer t.v.Pop() 1500 | 1501 | n.Accept(t.v) 1502 | 1503 | t.Traverse(n.Expr) 1504 | } 1505 | 1506 | func (t *Traverser) ExprCastInt(n *ast.ExprCastInt) { 1507 | t.v.Push(Item{Name: "(int)", Type: "break", Vertex: n}) 1508 | defer t.v.Pop() 1509 | 1510 | n.Accept(t.v) 1511 | 1512 | t.Traverse(n.Expr) 1513 | } 1514 | 1515 | func (t *Traverser) ExprCastObject(n *ast.ExprCastObject) { 1516 | n.Accept(t.v) 1517 | 1518 | t.Traverse(n.Expr) 1519 | } 1520 | 1521 | func (t *Traverser) ExprCastString(n *ast.ExprCastString) { 1522 | n.Accept(t.v) 1523 | 1524 | t.Traverse(n.Expr) 1525 | } 1526 | 1527 | func (t *Traverser) ExprCastUnset(n *ast.ExprCastUnset) { 1528 | t.v.Push(Item{Name: "(unset)", Type: "break", Vertex: n}) 1529 | defer t.v.Pop() 1530 | 1531 | n.Accept(t.v) 1532 | 1533 | t.Traverse(n.Expr) 1534 | } 1535 | 1536 | func (t *Traverser) ExprMatch(n *ast.ExprMatch) { 1537 | n.Accept(t.v) 1538 | 1539 | t.Traverse(n.Expr) 1540 | for _, nn := range n.Arms { 1541 | nn.Accept(t) 1542 | } 1543 | } 1544 | 1545 | func (t *Traverser) ExprThrow(n *ast.ExprThrow) { 1546 | n.Accept(t.v) 1547 | 1548 | t.Traverse(n.Expr) 1549 | } 1550 | 1551 | func (t *Traverser) ScalarDnumber(n *ast.ScalarDnumber) { 1552 | n.Accept(t.v) 1553 | } 1554 | 1555 | func (t *Traverser) ScalarEncapsed(n *ast.ScalarEncapsed) { 1556 | t.v.Push(Item{Name: "MAGICQUOTES", Type: "filter", Vertex: n}) 1557 | defer t.v.Pop() 1558 | 1559 | n.Accept(t.v) 1560 | 1561 | for _, nn := range n.Parts { 1562 | nn.Accept(t) 1563 | } 1564 | } 1565 | 1566 | func (t *Traverser) ScalarEncapsedStringPart(n *ast.ScalarEncapsedStringPart) { 1567 | n.Accept(t.v) 1568 | } 1569 | 1570 | func (t *Traverser) ScalarEncapsedStringVar(n *ast.ScalarEncapsedStringVar) { 1571 | n.Accept(t.v) 1572 | 1573 | t.Traverse(n.Name) 1574 | t.Traverse(n.Dim) 1575 | } 1576 | 1577 | func (t *Traverser) ScalarEncapsedStringBrackets(n *ast.ScalarEncapsedStringBrackets) { 1578 | n.Accept(t.v) 1579 | 1580 | t.Traverse(n.Var) 1581 | } 1582 | 1583 | func (t *Traverser) ScalarHeredoc(n *ast.ScalarHeredoc) { 1584 | n.Accept(t.v) 1585 | 1586 | for _, nn := range n.Parts { 1587 | nn.Accept(t) 1588 | } 1589 | } 1590 | 1591 | func (t *Traverser) ScalarLnumber(n *ast.ScalarLnumber) { 1592 | n.Accept(t.v) 1593 | } 1594 | 1595 | func (t *Traverser) ScalarMagicConstant(n *ast.ScalarMagicConstant) { 1596 | n.Accept(t.v) 1597 | } 1598 | 1599 | func (t *Traverser) ScalarString(n *ast.ScalarString) { 1600 | n.Accept(t.v) 1601 | } 1602 | 1603 | func (t *Traverser) NameName(n *ast.Name) { 1604 | n.Accept(t.v) 1605 | 1606 | for _, nn := range n.Parts { 1607 | nn.Accept(t) 1608 | } 1609 | } 1610 | 1611 | func (t *Traverser) NameFullyQualified(n *ast.NameFullyQualified) { 1612 | n.Accept(t.v) 1613 | 1614 | for _, nn := range n.Parts { 1615 | nn.Accept(t) 1616 | } 1617 | } 1618 | 1619 | func (t *Traverser) NameRelative(n *ast.NameRelative) { 1620 | n.Accept(t.v) 1621 | 1622 | for _, nn := range n.Parts { 1623 | nn.Accept(t) 1624 | } 1625 | } 1626 | 1627 | func (t *Traverser) NameNamePart(n *ast.NamePart) { 1628 | n.Accept(t.v) 1629 | } 1630 | --------------------------------------------------------------------------------