mirror of https://github.com/actions/toolkit
Prevent accidental file matching when glob contains trailing slash (#805)
* Draft Solution * Update internal-globber.ts * Cleanup * Fix Test * Cleanuppull/809/head
parent
b33912b7cc
commit
98549fbf21
|
@ -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>
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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}`
|
||||||
|
|
Loading…
Reference in New Issue