gitnexus 1.6.3-rc.25 → 1.6.3-rc.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,27 +1,71 @@
|
|
|
1
1
|
import PHP from 'tree-sitter-php';
|
|
2
2
|
import { compilePatterns, runCompiledPatterns, unquoteLiteral, } from '../tree-sitter-scanner.js';
|
|
3
3
|
/**
|
|
4
|
-
* PHP HTTP plugin
|
|
4
|
+
* PHP HTTP plugin.
|
|
5
|
+
*
|
|
6
|
+
* Providers:
|
|
7
|
+
* - Laravel `Route::get/post/...`
|
|
8
|
+
*
|
|
9
|
+
* Consumers (string-literal URLs only):
|
|
10
|
+
* - Laravel HTTP client: `Http::get/post/put/delete/patch($url)`
|
|
11
|
+
* - Guzzle / generic object method: `$client->get/post/...($url)`
|
|
12
|
+
* - `file_get_contents($url)`
|
|
5
13
|
*
|
|
6
14
|
* The pipeline already uses `PHP.php_only` for ingesting plain `.php`
|
|
7
15
|
* files (see `core/tree-sitter/parser-loader.ts`), and we do the same
|
|
8
16
|
* here so Laravel route files are parsed with the right grammar dialect.
|
|
17
|
+
*
|
|
18
|
+
* Scope notes: consumer patterns match string literals only. URLs built
|
|
19
|
+
* via binary concatenation (`$base . '/path'`), `sprintf`, or config
|
|
20
|
+
* lookup (`config('services.foo.base').'/path'`) are intentionally left
|
|
21
|
+
* for a follow-up — they require constant-folding the surrounding
|
|
22
|
+
* scope to be meaningful.
|
|
9
23
|
*/
|
|
10
|
-
const
|
|
11
|
-
|
|
24
|
+
const LARAVEL_ROUTE_SPEC = {
|
|
25
|
+
meta: {},
|
|
26
|
+
query: `
|
|
27
|
+
(scoped_call_expression
|
|
28
|
+
scope: (name) @scope (#eq? @scope "Route")
|
|
29
|
+
name: (name) @method (#match? @method "^(get|post|put|delete|patch)$")
|
|
30
|
+
arguments: (arguments . (argument (string) @path)))
|
|
31
|
+
`,
|
|
32
|
+
};
|
|
33
|
+
const HTTP_FACADE_SPEC = {
|
|
34
|
+
meta: {},
|
|
35
|
+
query: `
|
|
36
|
+
(scoped_call_expression
|
|
37
|
+
scope: (name) @scope (#eq? @scope "Http")
|
|
38
|
+
name: (name) @method (#match? @method "^(get|post|put|delete|patch)$")
|
|
39
|
+
arguments: (arguments . (argument (string) @path)))
|
|
40
|
+
`,
|
|
41
|
+
};
|
|
42
|
+
const GUZZLE_MEMBER_SPEC = {
|
|
43
|
+
meta: {},
|
|
44
|
+
query: `
|
|
45
|
+
(member_call_expression
|
|
46
|
+
name: (name) @method (#match? @method "^(get|post|put|delete|patch)$")
|
|
47
|
+
arguments: (arguments . (argument (string) @path)))
|
|
48
|
+
`,
|
|
49
|
+
};
|
|
50
|
+
const FILE_GET_CONTENTS_SPEC = {
|
|
51
|
+
meta: {},
|
|
52
|
+
query: `
|
|
53
|
+
(function_call_expression
|
|
54
|
+
function: (name) @fn (#eq? @fn "file_get_contents")
|
|
55
|
+
arguments: (arguments . (argument (string) @path)))
|
|
56
|
+
`,
|
|
57
|
+
};
|
|
58
|
+
const mk = (spec, suffix) => compilePatterns({
|
|
59
|
+
name: `php-${suffix}`,
|
|
12
60
|
language: PHP.php_only,
|
|
13
|
-
patterns: [
|
|
14
|
-
{
|
|
15
|
-
meta: {},
|
|
16
|
-
query: `
|
|
17
|
-
(scoped_call_expression
|
|
18
|
-
scope: (name) @scope (#eq? @scope "Route")
|
|
19
|
-
name: (name) @method (#match? @method "^(get|post|put|delete|patch)$")
|
|
20
|
-
arguments: (arguments . (argument (string) @path)))
|
|
21
|
-
`,
|
|
22
|
-
},
|
|
23
|
-
],
|
|
61
|
+
patterns: [spec],
|
|
24
62
|
});
|
|
63
|
+
const PHP_PATTERNS = {
|
|
64
|
+
laravelRoute: mk(LARAVEL_ROUTE_SPEC, 'laravel-route'),
|
|
65
|
+
httpFacade: mk(HTTP_FACADE_SPEC, 'http-facade'),
|
|
66
|
+
guzzleMember: mk(GUZZLE_MEMBER_SPEC, 'guzzle-member'),
|
|
67
|
+
fileGetContents: mk(FILE_GET_CONTENTS_SPEC, 'file-get-contents'),
|
|
68
|
+
};
|
|
25
69
|
/**
|
|
26
70
|
* Extract the inner text of a PHP `string` node. The tree-sitter-php
|
|
27
71
|
* grammar wraps single / double-quoted literals differently depending
|
|
@@ -30,12 +74,9 @@ const LARAVEL_PATTERNS = compilePatterns({
|
|
|
30
74
|
* child nodes.
|
|
31
75
|
*/
|
|
32
76
|
function phpStringText(node) {
|
|
33
|
-
// Most single-quoted strings expose their inner content through the
|
|
34
|
-
// full node text (including quotes), which unquoteLiteral strips.
|
|
35
77
|
const direct = unquoteLiteral(node.text);
|
|
36
78
|
if (direct !== null && direct !== node.text)
|
|
37
79
|
return direct;
|
|
38
|
-
// Fall back to child string_content / string_value node if present.
|
|
39
80
|
for (const child of node.children) {
|
|
40
81
|
if (child.type === 'string_content' || child.type === 'string_value') {
|
|
41
82
|
return child.text;
|
|
@@ -43,12 +84,29 @@ function phpStringText(node) {
|
|
|
43
84
|
}
|
|
44
85
|
return direct;
|
|
45
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* HTTP client helpers (`Http::`, Guzzle) are almost always called with
|
|
89
|
+
* a path relative to a configured base URL, or a full URL. File paths
|
|
90
|
+
* are rare. Accept both relative (`/api/...`) and absolute (`http(s)://`).
|
|
91
|
+
*/
|
|
92
|
+
function isHttpClientPath(path) {
|
|
93
|
+
return path.startsWith('/') || path.startsWith('http://') || path.startsWith('https://');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* `file_get_contents` is used for both HTTP and filesystem reads. Only
|
|
97
|
+
* emit a consumer contract when the URL is an absolute HTTP(S) URL to
|
|
98
|
+
* avoid false positives for local file paths and stream wrappers
|
|
99
|
+
* (`php://input`, `file://`, `data:`, ...).
|
|
100
|
+
*/
|
|
101
|
+
function isHttpUrlLiteral(path) {
|
|
102
|
+
return path.startsWith('http://') || path.startsWith('https://');
|
|
103
|
+
}
|
|
46
104
|
export const PHP_HTTP_PLUGIN = {
|
|
47
105
|
name: 'php-http',
|
|
48
106
|
language: PHP.php_only,
|
|
49
107
|
scan(tree) {
|
|
50
108
|
const out = [];
|
|
51
|
-
for (const match of runCompiledPatterns(
|
|
109
|
+
for (const match of runCompiledPatterns(PHP_PATTERNS.laravelRoute, tree)) {
|
|
52
110
|
const methodNode = match.captures.method;
|
|
53
111
|
const pathNode = match.captures.path;
|
|
54
112
|
if (!methodNode || !pathNode)
|
|
@@ -65,6 +123,56 @@ export const PHP_HTTP_PLUGIN = {
|
|
|
65
123
|
confidence: 0.8,
|
|
66
124
|
});
|
|
67
125
|
}
|
|
126
|
+
for (const match of runCompiledPatterns(PHP_PATTERNS.httpFacade, tree)) {
|
|
127
|
+
const methodNode = match.captures.method;
|
|
128
|
+
const pathNode = match.captures.path;
|
|
129
|
+
if (!methodNode || !pathNode)
|
|
130
|
+
continue;
|
|
131
|
+
const path = phpStringText(pathNode);
|
|
132
|
+
if (path === null || !isHttpClientPath(path))
|
|
133
|
+
continue;
|
|
134
|
+
out.push({
|
|
135
|
+
role: 'consumer',
|
|
136
|
+
framework: 'laravel-http',
|
|
137
|
+
method: methodNode.text.toUpperCase(),
|
|
138
|
+
path,
|
|
139
|
+
name: null,
|
|
140
|
+
confidence: 0.7,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
for (const match of runCompiledPatterns(PHP_PATTERNS.guzzleMember, tree)) {
|
|
144
|
+
const methodNode = match.captures.method;
|
|
145
|
+
const pathNode = match.captures.path;
|
|
146
|
+
if (!methodNode || !pathNode)
|
|
147
|
+
continue;
|
|
148
|
+
const path = phpStringText(pathNode);
|
|
149
|
+
if (path === null || !isHttpClientPath(path))
|
|
150
|
+
continue;
|
|
151
|
+
out.push({
|
|
152
|
+
role: 'consumer',
|
|
153
|
+
framework: 'guzzle',
|
|
154
|
+
method: methodNode.text.toUpperCase(),
|
|
155
|
+
path,
|
|
156
|
+
name: null,
|
|
157
|
+
confidence: 0.7,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
for (const match of runCompiledPatterns(PHP_PATTERNS.fileGetContents, tree)) {
|
|
161
|
+
const pathNode = match.captures.path;
|
|
162
|
+
if (!pathNode)
|
|
163
|
+
continue;
|
|
164
|
+
const path = phpStringText(pathNode);
|
|
165
|
+
if (path === null || !isHttpUrlLiteral(path))
|
|
166
|
+
continue;
|
|
167
|
+
out.push({
|
|
168
|
+
role: 'consumer',
|
|
169
|
+
framework: 'file-get-contents',
|
|
170
|
+
method: 'GET',
|
|
171
|
+
path,
|
|
172
|
+
name: null,
|
|
173
|
+
confidence: 0.7,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
68
176
|
return out;
|
|
69
177
|
},
|
|
70
178
|
};
|
package/package.json
CHANGED