@mh-alikhani/bunready 0.3.0 → 0.3.1
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.
- package/CHANGELOG.md +15 -1
- package/package.json +1 -1
- package/src/scanner/semver.ts +6 -6
- package/src/scanner/workspaces.ts +27 -5
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
Nothing yet.
|
|
11
11
|
|
|
12
|
+
## [0.3.1] - 2026-09-19
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Three polynomial-time regular expressions, reported by CodeQL as
|
|
17
|
+
`js/polynomial-redos`. bunready parses files it does not control - a
|
|
18
|
+
`pnpm-workspace.yaml` from a repository being scanned, a semver range in its
|
|
19
|
+
`engines` - so a pathological string could have made a scan take quadratic
|
|
20
|
+
time. The pnpm list item and the semver operator split are now parsed
|
|
21
|
+
directly, and trailing-slash stripping is a linear loop instead of `/+$`.
|
|
22
|
+
Behaviour is unchanged; `tests/workspaces.test.ts` and `tests/semver.test.ts`
|
|
23
|
+
cover the affected paths.
|
|
24
|
+
|
|
12
25
|
## [0.3.0] - 2026-09-18
|
|
13
26
|
|
|
14
27
|
### Added
|
|
@@ -188,7 +201,8 @@ Nothing yet.
|
|
|
188
201
|
and that claim needs a primary source. Until then the rule reports what the
|
|
189
202
|
repository imports and cites the compatibility table.
|
|
190
203
|
|
|
191
|
-
[Unreleased]: https://github.com/MHAlikhani/bunready/compare/v0.3.
|
|
204
|
+
[Unreleased]: https://github.com/MHAlikhani/bunready/compare/v0.3.1...HEAD
|
|
205
|
+
[0.3.1]: https://github.com/MHAlikhani/bunready/releases/tag/v0.3.1
|
|
192
206
|
[0.3.0]: https://github.com/MHAlikhani/bunready/releases/tag/v0.3.0
|
|
193
207
|
[0.2.0]: https://github.com/MHAlikhani/bunready/releases/tag/v0.2.0
|
|
194
208
|
[0.1.0]: https://github.com/MHAlikhani/bunready/releases/tag/v0.1.0
|
package/package.json
CHANGED
package/src/scanner/semver.ts
CHANGED
|
@@ -92,12 +92,12 @@ function comparatorsForToken(token: string): Comparator[] | undefined {
|
|
|
92
92
|
return [];
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const operator = operatorMatch[1];
|
|
100
|
-
const rest = operatorMatch[
|
|
95
|
+
// Anchored-prefix parse rather than a trailing \s*(.*)$, which backtracks
|
|
96
|
+
// quadratically on a long run of spaces - and the range comes from a
|
|
97
|
+
// repository bunready does not control.
|
|
98
|
+
const operatorMatch = /^(>=|<=|>|<|=|\^|~)?/.exec(trimmed);
|
|
99
|
+
const operator = operatorMatch?.[1];
|
|
100
|
+
const rest = trimmed.slice(operatorMatch?.[0].length ?? 0).trim();
|
|
101
101
|
if (rest === "" || rest === "*" || rest === "x") {
|
|
102
102
|
return [];
|
|
103
103
|
}
|
|
@@ -54,9 +54,22 @@ export function patternsFromPnpmWorkspace(text: string): string[] {
|
|
|
54
54
|
if (!inPackages) {
|
|
55
55
|
continue;
|
|
56
56
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
// Walked rather than matched: a pattern mixing a dash run, a space run and a
|
|
58
|
+
// catch-all is ambiguous, and CodeQL is right that it backtracks on a string
|
|
59
|
+
// of dashes and spaces. The value is trimmed here instead.
|
|
60
|
+
let cursor = 0;
|
|
61
|
+
while (cursor < trimmed.length && trimmed[cursor] === "-") {
|
|
62
|
+
cursor += 1;
|
|
63
|
+
}
|
|
64
|
+
const value =
|
|
65
|
+
cursor === 0
|
|
66
|
+
? ""
|
|
67
|
+
: trimmed
|
|
68
|
+
.slice(cursor)
|
|
69
|
+
.trim()
|
|
70
|
+
.replace(/^['"]|['"]$/g, "");
|
|
71
|
+
if (value !== "") {
|
|
72
|
+
patterns.push(value);
|
|
60
73
|
}
|
|
61
74
|
}
|
|
62
75
|
|
|
@@ -123,6 +136,15 @@ async function expandGlob(root: string, segments: string[], fs: FileSystem): Pro
|
|
|
123
136
|
return prefixes.filter((prefix) => prefix !== "" && prefix !== ".");
|
|
124
137
|
}
|
|
125
138
|
|
|
139
|
+
/** Linear replacement for `/+$`, which backtracks on a run of slashes. */
|
|
140
|
+
function stripTrailingSlashes(value: string): string {
|
|
141
|
+
let end = value.length;
|
|
142
|
+
while (end > 0 && value[end - 1] === "/") {
|
|
143
|
+
end -= 1;
|
|
144
|
+
}
|
|
145
|
+
return value.slice(0, end);
|
|
146
|
+
}
|
|
147
|
+
|
|
126
148
|
export async function findWorkspacePackages(
|
|
127
149
|
root: string,
|
|
128
150
|
patterns: readonly string[],
|
|
@@ -131,7 +153,7 @@ export async function findWorkspacePackages(
|
|
|
131
153
|
const found = new Map<string, WorkspacePackage>();
|
|
132
154
|
|
|
133
155
|
for (const pattern of patterns) {
|
|
134
|
-
const cleaned = pattern.replace(/\\/g, "/")
|
|
156
|
+
const cleaned = stripTrailingSlashes(pattern.replace(/\\/g, "/")).replace(/^\.\//, "");
|
|
135
157
|
if (cleaned === "" || cleaned === ".") {
|
|
136
158
|
continue;
|
|
137
159
|
}
|
|
@@ -165,6 +187,6 @@ export async function readPnpmWorkspace(root: string, fs: FileSystem): Promise<s
|
|
|
165
187
|
|
|
166
188
|
/** Keep only the packages matching a scope string, by relative path or name. */
|
|
167
189
|
export function scopeMatches(relative: string, scope: string): boolean {
|
|
168
|
-
const needle = scope.replace(/\\/g, "/").replace(/^\.\//, "")
|
|
190
|
+
const needle = stripTrailingSlashes(scope.replace(/\\/g, "/")).replace(/^\.\//, "");
|
|
169
191
|
return relative === needle || relative.includes(needle);
|
|
170
192
|
}
|