Skip to content

Commit 50eb31b

Browse files
committed
flatten globs
1 parent 84b1562 commit 50eb31b

File tree

3 files changed

+152
-37
lines changed

3 files changed

+152
-37
lines changed

package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,7 @@
3535
"activationEvents": [
3636
"workspaceContains:**/[pP][aA][cC][kK][aA][gG][eE].[jJ][sS][oO][nN]",
3737
"workspaceContains:**/[sS][oO][cC][kK][eE][tT].[yY][mM][lL]",
38-
"workspaceContains:**/*[rR][eE][qQ][uU][iI][rR][eE][mM][eE][nN][tT][sS].[tT][xX][tT]",
39-
"workspaceContains:**/*[rR][eE][qQ][uU][iI][rR][eE][mM][eE][nN][tT][sS]/*.[tT][xX][tT]",
40-
"workspaceContains:**/*[rR][eE][qQ][uU][iI][rR][eE][mM][eE][nN][tT][sS]-*.[tT][xX][tT]",
41-
"workspaceContains:**/*[rR][eE][qQ][uU][iI][rR][eE][mM][eE][nN][tT][sS].[fF][rR][oO][zZ][eE][nN]",
38+
"workspaceContains:**/[rR][eE][qQ][uU][iI][rR][eE][mM][eE][nN][tT][sS].[tT][xX][tT]",
4239
"workspaceContains:**/[pP][yY][pP][rR][oO][jJ][eE][cC][tT].[tT][oO][mM][lL]",
4340
"workspaceContains:**/[pP][iI][pP][fF][iI][lL][eE]",
4441
"workspaceContains:**/[gG][oO].[mM][oO][dD]",

src/data/glob-patterns.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { IncomingMessage } from 'node:http';
22
import * as https from 'node:https';
33
import { once } from 'node:stream';
44
import { text } from 'stream/consumers';
5+
import { flattenGlob } from '../util'
56

67
export type GlobPatterns = Record<string, Record<string, { pattern: string }>>
78

@@ -36,7 +37,7 @@ export async function getGlobPatterns() {
3637
for (const eco in result) {
3738
for (const name in result[eco]) {
3839
const target = result[eco][name];
39-
target.pattern = caseDesensitize(target.pattern)
40+
target.pattern = caseDesensitize(flattenGlob(target.pattern))
4041
}
4142
}
4243
return result
@@ -48,4 +49,4 @@ export async function getGlobPatterns() {
4849
})
4950
}
5051
return globPatternsPromise
51-
}
52+
}

src/util.ts

Lines changed: 148 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import type { SocketYml } from '@socketsecurity/config';
2-
import * as toml from 'toml-eslint-parser';
3-
import * as vscode from 'vscode';
4-
import { IssueRules, mergeDefaults, mergeRules, ruleStrength } from './data/socket-api-config';
1+
import type { SocketYml } from '@socketsecurity/config'
2+
import * as toml from 'toml-eslint-parser'
3+
import * as vscode from 'vscode'
4+
import { IssueRules, mergeDefaults, mergeRules, ruleStrength } from './data/socket-api-config'
55

66
export const DIAGNOSTIC_SOURCE_STR = 'SocketSecurity'
77
export const EXTENSION_PREFIX = 'socket-security'
88

9-
const SEVERITY_LEVELS = ['low', 'middle', 'high', 'critical'];
9+
const SEVERITY_LEVELS = ['low', 'middle', 'high', 'critical']
1010

1111
export function getDiagnosticSeverity(type: string, severity: string, enforcedRules: IssueRules, issueRules: IssueRules, socketYamlConfig: SocketYml): vscode.DiagnosticSeverity | null {
1212
const fullRules: IssueRules = mergeDefaults(
@@ -16,7 +16,7 @@ export function getDiagnosticSeverity(type: string, severity: string, enforcedRu
1616
const editorConfig = vscode.workspace.getConfiguration(EXTENSION_PREFIX)
1717
const handling = ruleStrength(fullRules[type])
1818
if (handling < 2) return null
19-
19+
2020
const curLevel = SEVERITY_LEVELS.indexOf(severity)
2121
const minLevel = SEVERITY_LEVELS.indexOf(editorConfig.get('minIssueLevel') as string)
2222
if (curLevel >= minLevel) {
@@ -67,7 +67,7 @@ export function getWorkspaceFolderURI(from: vscode.Uri) {
6767
return vscode.workspace.getWorkspaceFolder(from)?.uri
6868
}
6969

70-
type ListenerEventData<Data> = {uri: vscode.Uri, data: Data, defaulted: boolean}
70+
type ListenerEventData<Data> = { uri: vscode.Uri, data: Data, defaulted: boolean }
7171
/**
7272
* Setup data to be isolated and associated with workspace folders, note: should only be used for data
7373
* DO NOT SETUP listeners inside the callbacks because things like watchers are global and not per workspace
@@ -93,7 +93,7 @@ export class WorkspaceData<Data> {
9393
try {
9494
this.onWorkspaceFolder(folder.uri)
9595
} catch (e) {
96-
console.error(e);
96+
console.error(e)
9797
}
9898
}
9999
})
@@ -104,14 +104,14 @@ export class WorkspaceData<Data> {
104104
* @param from Uri within the workspace to recalculate will be used to din the workspace
105105
*/
106106
recalculateDataForUri(from: vscode.Uri, clear: boolean = false) {
107-
const workspaceFolderURI = getWorkspaceFolderURI(from);
107+
const workspaceFolderURI = getWorkspaceFolderURI(from)
108108
if (!workspaceFolderURI) {
109-
return;
109+
return
110110
}
111111
if (clear) {
112-
this.workspaceScopeData.delete(workspaceFolderURI.fsPath);
112+
this.workspaceScopeData.delete(workspaceFolderURI.fsPath)
113113
}
114-
this.onWorkspaceFolder(workspaceFolderURI);
114+
this.onWorkspaceFolder(workspaceFolderURI)
115115
}
116116

117117
/**
@@ -127,7 +127,7 @@ export class WorkspaceData<Data> {
127127
try {
128128
this.onWorkspaceFolder(folder.uri)
129129
} catch (e) {
130-
console.error(e);
130+
console.error(e)
131131
}
132132
}
133133
}
@@ -138,7 +138,7 @@ export class WorkspaceData<Data> {
138138
data: Data,
139139
defaulted: boolean,
140140
} {
141-
const workspaceFolderURI = getWorkspaceFolderURI(from);
141+
const workspaceFolderURI = getWorkspaceFolderURI(from)
142142
if (!workspaceFolderURI) {
143143
return {
144144
uri: from,
@@ -156,24 +156,24 @@ export class WorkspaceData<Data> {
156156
}
157157

158158
update(from: vscode.Uri, value: Data | undefined) {
159-
const workspaceFolderURI = getWorkspaceFolderURI(from);
159+
const workspaceFolderURI = getWorkspaceFolderURI(from)
160160
if (!workspaceFolderURI) {
161161
return
162162
}
163163
if (value !== undefined) {
164-
this.workspaceScopeData.set(workspaceFolderURI.fsPath, value);
164+
this.workspaceScopeData.set(workspaceFolderURI.fsPath, value)
165165
} else {
166-
this.workspaceScopeData.delete(workspaceFolderURI.fsPath);
166+
this.workspaceScopeData.delete(workspaceFolderURI.fsPath)
167167
}
168168
this.fire(workspaceFolderURI)
169169
}
170170

171171
fire(uri: vscode.Uri) {
172-
const workspaceFolderURI = getWorkspaceFolderURI(uri);
172+
const workspaceFolderURI = getWorkspaceFolderURI(uri)
173173
if (!workspaceFolderURI) {
174174
return
175175
}
176-
const data = this.workspaceScopeData.get(workspaceFolderURI.fsPath);
176+
const data = this.workspaceScopeData.get(workspaceFolderURI.fsPath)
177177
for (const listenerData of this.listeners.values()) {
178178
if (listenerData.uri === null || (getWorkspaceFolderURI(listenerData.uri)?.fsPath === workspaceFolderURI.fsPath)) {
179179
listenerData.emitter.fire({
@@ -188,16 +188,16 @@ export class WorkspaceData<Data> {
188188
on(from: vscode.Uri | null, fn: (e: ListenerEventData<Data>) => void): vscode.Disposable {
189189
const {
190190
listeners
191-
} = this;
191+
} = this
192192
let key = from ? from.fsPath : null
193193
let existing = listeners.get(key) ?? {
194194
uri: from,
195195
refCount: 0,
196196
emitter: new vscode.EventEmitter()
197197
}
198-
existing.refCount++;
199-
let unwatch = existing.emitter.event(fn);
200-
listeners.set(key, existing);
198+
existing.refCount++
199+
let unwatch = existing.emitter.event(fn)
200+
listeners.set(key, existing)
201201
return {
202202
dispose() {
203203
const existing = listeners.get(key)
@@ -215,24 +215,141 @@ export class WorkspaceData<Data> {
215215
}
216216

217217
export function traverseTOMLKeys(src: toml.AST.TOMLProgram, cb: (key: toml.AST.TOMLKey, path: (string | number)[]) => unknown) {
218-
const curPath: (string | number)[] = [];
218+
const curPath: (string | number)[] = []
219219

220220
toml.traverseNodes(src, {
221221
enterNode(node) {
222222
if (node.type === 'TOMLKeyValue') {
223-
curPath.push(...node.key.keys.map(k => k.type == 'TOMLBare' ? k.name : k.value));
223+
curPath.push(...node.key.keys.map(k => k.type == 'TOMLBare' ? k.name : k.value))
224224
} else if (node.type === 'TOMLTable') {
225-
curPath.push(...node.resolvedKey);
225+
curPath.push(...node.resolvedKey)
226226
} else if (node.type === 'TOMLKey') {
227-
cb(node, curPath);
227+
cb(node, curPath)
228228
}
229229
},
230230
leaveNode(node) {
231231
if (node.type === 'TOMLKeyValue') {
232-
curPath.length -= node.key.keys.length;
232+
curPath.length -= node.key.keys.length
233233
} else if (node.type === 'TOMLTable') {
234-
curPath.length -= node.resolvedKey.length;
234+
curPath.length -= node.resolvedKey.length
235+
}
236+
}
237+
})
238+
}
239+
240+
export function flattenGlob(glob: string) {
241+
type Item = Alternation | Concatenation | string
242+
class Alternation {
243+
alternates: Item[]
244+
constructor(items: Alternation['alternates'] = []) {
245+
this.alternates = items
246+
}
247+
push(item: Item) {
248+
this.alternates.push(item)
249+
}
250+
explode(): string[] {
251+
let options: string[] = []
252+
for (const alternate of this.alternates) {
253+
if (typeof alternate === 'string') {
254+
options.push(alternate)
255+
} else if (alternate instanceof Concatenation) {
256+
options.push(...alternate.explode())
257+
} else if (alternate instanceof Alternation) {
258+
options.push(...alternate.explode())
259+
}
260+
}
261+
return options
262+
}
263+
}
264+
265+
class Concatenation {
266+
segments: Item[]
267+
constructor(items: Concatenation['segments'] = []) {
268+
this.segments = items
269+
}
270+
push(item: Item) {
271+
this.segments.push(item)
272+
}
273+
explode(): string[] {
274+
let prefixed = ['']
275+
for (const segment of this.segments) {
276+
let suffixes: string[]
277+
if (typeof segment === 'string') {
278+
suffixes = [segment]
279+
} else if (segment instanceof Concatenation) {
280+
suffixes = segment.explode()
281+
} else if (segment instanceof Alternation) {
282+
suffixes = segment.explode()
283+
} else {
284+
throw new Error('unreachable')
285+
}
286+
if (suffixes.length > 0) {
287+
prefixed = prefixed.flatMap(prefix => {
288+
return suffixes.map(suffix => `${prefix}${suffix}`)
289+
})
290+
}
291+
}
292+
return prefixed
293+
}
294+
}
295+
296+
function explode(str: string) {
297+
let finder = /\\[\s\S]|[{},]/g
298+
let root = new Concatenation()
299+
let stack: Array<Alternation | Concatenation> = [root]
300+
let right = 0
301+
let match = finder.exec(str)
302+
while (match) {
303+
try {
304+
let c = match[0]
305+
if (c[0] === '\\') {
306+
let prefix = str.slice(right, match.index) + c
307+
let current = stack.at(-1)!
308+
current.push(prefix)
309+
continue
310+
} else if (c === '{') {
311+
let prefix = str.slice(right, match.index)
312+
let a = new Alternation()
313+
let c = new Concatenation()
314+
let current = stack.at(-1)!
315+
current.push(prefix)
316+
current.push(a)
317+
a.push(c)
318+
stack.push(a)
319+
stack.push(c)
320+
} else if (c === '}') {
321+
let current = stack.at(-1)!
322+
if (stack.length <= 1) {
323+
current.push(c)
324+
continue
325+
}
326+
let tail = str.slice(right, match.index)
327+
let concat = stack.pop()!
328+
let alternate = stack.pop()!
329+
concat.push(tail)
330+
} else if (c === ',') {
331+
let current = stack.at(-1)!
332+
if (stack.length <= 1) {
333+
current.push(c)
334+
continue
335+
}
336+
let tail = str.slice(right, match.index)
337+
let concat = stack.pop()!
338+
concat.push(tail)
339+
let next = new Concatenation()
340+
stack.at(-1)!.push(next)
341+
stack.push(next)
342+
}
343+
}
344+
finally {
345+
right = finder.lastIndex
346+
match = finder.exec(str)
235347
}
236348
}
237-
});
238-
}
349+
let tail = str.slice(right)
350+
stack.at(-1)!.push(tail)
351+
return root.explode()
352+
}
353+
354+
return `{${explode(glob).join(',')}}`
355+
}

0 commit comments

Comments
 (0)