1
0
Fork 0

Prevent accidental file matching when glob contains trailing slash (#805)

* Draft Solution

* Update internal-globber.ts

* Cleanup

* Fix Test

* Cleanup
pull/809/head
Luke Tomlinson 2021-05-14 14:12:26 -04:00 committed by GitHub
parent b33912b7cc
commit 98549fbf21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 6 deletions

View File

@ -97,6 +97,24 @@ describe('globber', () => {
]) ])
}) })
it('does not match file with trailing slash when implicitDescendants=true', async () => {
// Create the following layout:
// <root>
// <root>/file
const root = path.join(
getTestTemp(),
'defaults-to-implicit-descendants-true'
)
const filePath = path.join(root, 'file')
await fs.mkdir(root, {recursive: true})
await fs.writeFile(filePath, 'test file content')
const itemPaths = await glob(`${filePath}/`, {})
expect(itemPaths).toEqual([])
})
it('defaults to omitBrokenSymbolicLinks=true', async () => { it('defaults to omitBrokenSymbolicLinks=true', async () => {
// Create the following layout: // Create the following layout:
// <root> // <root>

View File

@ -26,7 +26,7 @@ describe('pattern', () => {
it('escapes homedir', async () => { it('escapes homedir', async () => {
const home = path.join(getTestTemp(), 'home-with-[and]') const home = path.join(getTestTemp(), 'home-with-[and]')
await fs.mkdir(home, {recursive: true}) await fs.mkdir(home, {recursive: true})
const pattern = new Pattern('~/m*', undefined, home) const pattern = new Pattern('~/m*', false, undefined, home)
expect(pattern.searchPath).toBe(home) expect(pattern.searchPath).toBe(home)
expect(pattern.match(path.join(home, 'match'))).toBeTruthy() expect(pattern.match(path.join(home, 'match'))).toBeTruthy()

View File

@ -66,7 +66,6 @@ export class DefaultGlobber implements Globber {
async *globGenerator(): AsyncGenerator<string, void> { async *globGenerator(): AsyncGenerator<string, void> {
// Fill in defaults options // Fill in defaults options
const options = globOptionsHelper.getOptions(this.options) const options = globOptionsHelper.getOptions(this.options)
// Implicit descendants? // Implicit descendants?
const patterns: Pattern[] = [] const patterns: Pattern[] = []
for (const pattern of this.patterns) { for (const pattern of this.patterns) {
@ -77,12 +76,13 @@ export class DefaultGlobber implements Globber {
pattern.segments[pattern.segments.length - 1] !== '**') pattern.segments[pattern.segments.length - 1] !== '**')
) { ) {
patterns.push( patterns.push(
new Pattern(pattern.negate, pattern.segments.concat('**')) new Pattern(pattern.negate, true, pattern.segments.concat('**'))
) )
} }
} }
// Push the search paths // Push the search paths
const stack: SearchState[] = [] const stack: SearchState[] = []
for (const searchPath of patternHelper.getSearchPaths(patterns)) { for (const searchPath of patternHelper.getSearchPaths(patterns)) {
core.debug(`Search path '${searchPath}'`) core.debug(`Search path '${searchPath}'`)
@ -180,6 +180,7 @@ export class DefaultGlobber implements Globber {
} }
result.searchPaths.push(...patternHelper.getSearchPaths(result.patterns)) result.searchPaths.push(...patternHelper.getSearchPaths(result.patterns))
return result return result
} }

View File

@ -43,15 +43,31 @@ export class Pattern {
*/ */
private readonly rootRegExp: RegExp private readonly rootRegExp: RegExp
/**
* Indicates that the pattern is implicitly added as opposed to user specified.
*/
private readonly isImplicitPattern: boolean
/* eslint-disable no-dupe-class-members */ /* eslint-disable no-dupe-class-members */
// Disable no-dupe-class-members due to false positive for method overload // Disable no-dupe-class-members due to false positive for method overload
// https://github.com/typescript-eslint/typescript-eslint/issues/291 // https://github.com/typescript-eslint/typescript-eslint/issues/291
constructor(pattern: string) constructor(pattern: string)
constructor(pattern: string, segments: undefined, homedir: string) constructor(
constructor(negate: boolean, segments: string[]) pattern: string,
isImplicitPattern: boolean,
segments: undefined,
homedir: string
)
constructor(
negate: boolean,
isImplicitPattern: boolean,
segments: string[],
homedir?: string
)
constructor( constructor(
patternOrNegate: string | boolean, patternOrNegate: string | boolean,
isImplicitPattern: boolean = false,
segments?: string[], segments?: string[],
homedir?: string homedir?: string
) { ) {
@ -107,6 +123,8 @@ export class Pattern {
IS_WINDOWS ? 'i' : '' IS_WINDOWS ? 'i' : ''
) )
this.isImplicitPattern = isImplicitPattern
// Create minimatch // Create minimatch
const minimatchOptions: IMinimatchOptions = { const minimatchOptions: IMinimatchOptions = {
dot: true, dot: true,
@ -132,7 +150,7 @@ export class Pattern {
// Append a trailing slash. Otherwise Minimatch will not match the directory immediately // Append a trailing slash. Otherwise Minimatch will not match the directory immediately
// preceding the globstar. For example, given the pattern `/foo/**`, Minimatch returns // preceding the globstar. For example, given the pattern `/foo/**`, Minimatch returns
// false for `/foo` but returns true for `/foo/`. Append a trailing slash to handle that quirk. // false for `/foo` but returns true for `/foo/`. Append a trailing slash to handle that quirk.
if (!itemPath.endsWith(path.sep)) { if (!itemPath.endsWith(path.sep) && this.isImplicitPattern === false) {
// Note, this is safe because the constructor ensures the pattern has an absolute root. // Note, this is safe because the constructor ensures the pattern has an absolute root.
// For example, formats like C: and C:foo on Windows are resolved to an absolute root. // For example, formats like C: and C:foo on Windows are resolved to an absolute root.
itemPath = `${itemPath}${path.sep}` itemPath = `${itemPath}${path.sep}`