├── 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 |
--------------------------------------------------------------------------------