Skip to content

Commit 98549fb

Browse files
Prevent accidental file matching when glob contains trailing slash (#805)
* Draft Solution * Update internal-globber.ts * Cleanup * Fix Test * Cleanup
1 parent b33912b commit 98549fb

File tree

4 files changed

+43
-6
lines changed

4 files changed

+43
-6
lines changed

‎packages/glob/__tests__/internal-globber.test.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,24 @@ describe('globber', () => {
9797
])
9898
})
9999

100+
it('does not match file with trailing slash when implicitDescendants=true', async () => {
101+
// Create the following layout:
102+
// <root>
103+
// <root>/file
104+
const root = path.join(
105+
getTestTemp(),
106+
'defaults-to-implicit-descendants-true'
107+
)
108+
109+
const filePath = path.join(root, 'file')
110+
111+
await fs.mkdir(root, {recursive: true})
112+
await fs.writeFile(filePath, 'test file content')
113+
114+
const itemPaths = await glob(`${filePath}/`, {})
115+
expect(itemPaths).toEqual([])
116+
})
117+
100118
it('defaults to omitBrokenSymbolicLinks=true', async () => {
101119
// Create the following layout:
102120
// <root>

‎packages/glob/__tests__/internal-pattern.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe('pattern', () => {
2626
it('escapes homedir', async () => {
2727
const home = path.join(getTestTemp(), 'home-with-[and]')
2828
await fs.mkdir(home, {recursive: true})
29-
const pattern = new Pattern('~/m*', undefined, home)
29+
const pattern = new Pattern('~/m*', false, undefined, home)
3030

3131
expect(pattern.searchPath).toBe(home)
3232
expect(pattern.match(path.join(home, 'match'))).toBeTruthy()

‎packages/glob/src/internal-globber.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ export class DefaultGlobber implements Globber {
6666
async *globGenerator(): AsyncGenerator<string, void> {
6767
// Fill in defaults options
6868
const options = globOptionsHelper.getOptions(this.options)
69-
7069
// Implicit descendants?
7170
const patterns: Pattern[] = []
7271
for (const pattern of this.patterns) {
@@ -77,12 +76,13 @@ export class DefaultGlobber implements Globber {
7776
pattern.segments[pattern.segments.length - 1] !== '**')
7877
) {
7978
patterns.push(
80-
new Pattern(pattern.negate, pattern.segments.concat('**'))
79+
new Pattern(pattern.negate, true, pattern.segments.concat('**'))
8180
)
8281
}
8382
}
8483

8584
// Push the search paths
85+
8686
const stack: SearchState[] = []
8787
for (const searchPath of patternHelper.getSearchPaths(patterns)) {
8888
core.debug(`Search path '${searchPath}'`)
@@ -180,6 +180,7 @@ export class DefaultGlobber implements Globber {
180180
}
181181

182182
result.searchPaths.push(...patternHelper.getSearchPaths(result.patterns))
183+
183184
return result
184185
}
185186

‎packages/glob/src/internal-pattern.ts‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,31 @@ export class Pattern {
4343
*/
4444
private readonly rootRegExp: RegExp
4545

46+
/**
47+
* Indicates that the pattern is implicitly added as opposed to user specified.
48+
*/
49+
private readonly isImplicitPattern: boolean
50+
4651
/* eslint-disable no-dupe-class-members */
4752
// Disable no-dupe-class-members due to false positive for method overload
4853
// https://github.com/typescript-eslint/typescript-eslint/issues/291
4954

5055
constructor(pattern: string)
51-
constructor(pattern: string, segments: undefined, homedir: string)
52-
constructor(negate: boolean, segments: string[])
56+
constructor(
57+
pattern: string,
58+
isImplicitPattern: boolean,
59+
segments: undefined,
60+
homedir: string
61+
)
62+
constructor(
63+
negate: boolean,
64+
isImplicitPattern: boolean,
65+
segments: string[],
66+
homedir?: string
67+
)
5368
constructor(
5469
patternOrNegate: string | boolean,
70+
isImplicitPattern: boolean = false,
5571
segments?: string[],
5672
homedir?: string
5773
) {
@@ -107,6 +123,8 @@ export class Pattern {
107123
IS_WINDOWS ? 'i' : ''
108124
)
109125

126+
this.isImplicitPattern = isImplicitPattern
127+
110128
// Create minimatch
111129
const minimatchOptions: IMinimatchOptions = {
112130
dot: true,
@@ -132,7 +150,7 @@ export class Pattern {
132150
// Append a trailing slash. Otherwise Minimatch will not match the directory immediately
133151
// preceding the globstar. For example, given the pattern `/foo/**`, Minimatch returns
134152
// false for `/foo` but returns true for `/foo/`. Append a trailing slash to handle that quirk.
135-
if (!itemPath.endsWith(path.sep)) {
153+
if (!itemPath.endsWith(path.sep) && this.isImplicitPattern === false) {
136154
// Note, this is safe because the constructor ensures the pattern has an absolute root.
137155
// For example, formats like C: and C:foo on Windows are resolved to an absolute root.
138156
itemPath = `${itemPath}${path.sep}`

0 commit comments

Comments
 (0)