diff --git a/NOTICE.md b/NOTICE.md index f07b1831..0f07c0b4 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -850,7 +850,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - isexe, 2.0.0, ISC, - json-stringify-safe, 5.0.1, ISC, - lru-cache, 6.0.0, ISC, -- minimatch, 3.0.4, ISC, +- minimatch, 10.0.1, ISC, - octokit-auth-probot, 1.2.3, ISC, - once, 1.4.0, ISC, - probot, 11.0.6, ISC, diff --git a/README.md b/README.md index 5cd5eeb9..923b7d0f 100644 --- a/README.md +++ b/README.md @@ -64,16 +64,40 @@ The App listens to the following webhook events: If you rename a `` that corresponds to a repo, safe-settings will rename the repo to the new name. This behavior will take effect whether the env variable `BLOCK_REPO_RENAME_BY_HUMAN` is set or not. ### Restricting `safe-settings` to specific repos -`safe-settings` can be turned on only to a subset of repos by specifying them in the runtime settings file, `deployment-settings.yml`. If no file is specified, then the following repositories - `'admin', '.github', 'safe-settings'` are exempted by default. -A sample of `deployment-settings` file is found [here](docs/sample-settings/sample-deployment-settings.yml). - -To apply `safe-settings` __only__ to a specific list of repos, add them to the `restrictedRepos` section as `include` array. -To ignore `safe-settings` for a specific list of repos, add them to the `restrictedRepos` section as `exclude` array. +To restrict which repositories `safe-settings` can manage, create a `deployment-settings.yml` file. This file controls the app's scope throught the `restrictedRepos` configuration: + +```yml +# Using include/exclude +restrictedRepos: + include: + - api + - core-* # Matches `core-api`, `core-service`, etc. + exclude: + - admin + - .github + - safe-settings + - test-* # Matches `test-repo`, etc. + +# Or using simple array syntax for includes +restrictedRepos: + - admin + - .github + # ... +``` > [!NOTE] -> The `include` and `exclude` attributes support as well regular expressions. -> By default they look for regex, Example include: ['SQL'] will look apply to repos with SQL and SQL_ and SQL- etc if you want only SQL repo then use include:['^SQL$'] +> Pattern matching uses glob expressions, e.g use * for wildcards. + +When using `include` and `exclude`: + +- If `include` is specified, will **only** run on repositories that match pattern(s) +- If `exlcude` is specified, will run on all repositories **expect** those matching pattern(s) +- If both are specified, will run only on included repositories that are'nt excluded + +By default, if no configuration file is provided, `safe-settings` will excludes these repos: `admin`, `.github` and `safe-settings`. + +See our [deployment-settings.yml sample](docs/sample-settings/sample-deployment-settings.yml). ### Custom rules @@ -329,24 +353,28 @@ The following can be configured: - `Rulesets` - `Environments` - wait timer, required reviewers, prevent self review, protected branches deployment branch policy, custom deployment branch policy, variables, deployment protection rules -> [!important] -> It is possible to provide an `include` or `exclude` settings to restrict the `collaborators`, `teams`, `labels` to a list of repos or exclude a set of repos for a collaborator. -> The include/exclude pattern can also be for glob. For e.g.: -``` -teams: - - name: Myteam-admins - permission: admin - - name: Myteam-developers - permission: push - - name: Other-team - permission: push - include: - - '*-config' -``` -> Will only add `Other-team` to only `*-config` repos - See [`docs/sample-settings/settings.yml`](docs/sample-settings/settings.yml) for a sample settings file. +> [!note] +> When using `collaborators`, `teams` or `labels`, you can control which repositories they apply to using `include` and `exclude`: +> +> - If `include` is specified, settings will **only** apply to repositories that match those patterns +> - If `exclude` is specified, settings will apply to all repositories **except** those matching the patterns +> - If both are specified, `exclude` takes precedence over `include` but `include` patterns will still be respected +> +> Pattern matching uses glob expressions, e.g use * for wildcards. For example: +> +> ```yml +> teams: +> - name: Myteam-admins +> permission: admin +> - name: Myteam-developers +> permission: push +> - name: Other-team +> permission: push +> include: +> - '*-config' +> ``` ### Additional values diff --git a/docs/sample-settings/sample-deployment-settings.yml b/docs/sample-settings/sample-deployment-settings.yml index 4244b45e..6164d438 100644 --- a/docs/sample-settings/sample-deployment-settings.yml +++ b/docs/sample-settings/sample-deployment-settings.yml @@ -1,9 +1,19 @@ +# This is a sample deployment settings file +# See the documentation for more details on how to use this file + +# If no file is specified, the following repositories are excluded by default +# restrictedRepos: ['admin', '.github', 'safe-settings'] + restrictedRepos: - # You can exclude certain repos from safe-settings processing - # If no file is specified, then the following repositories - 'admin', '.github', 'safe-settings' are exempted by default - exclude: ['^admin$', '^\.github$', '^safe-settings$', '.*-test'] - # Alternatively you can only include certain repos - include: ['^test$'] + exclude: + - admin + - .github + - safe-settings + - admin-* + include: + - docs + - core-* + configvalidators: - plugin: collaborators error: | @@ -11,6 +21,7 @@ configvalidators: script: | console.log(`baseConfig ${JSON.stringify(baseconfig)}`) return baseconfig.permission != 'admin' + overridevalidators: - plugin: branches error: | diff --git a/index.js b/index.js index 95b02ad6..bd860832 100644 --- a/index.js +++ b/index.js @@ -134,75 +134,60 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) => } function getAllChangedSubOrgConfigs (payload) { - const settingPattern = Settings.SUB_ORG_PATTERN - // Changes will be an array of files that were added - const added = payload.commits.map(c => { - return (c.added.filter(s => { - robot.log.debug(JSON.stringify(s)) - return (s.search(settingPattern) >= 0) - })) - }).flat(2) - const modified = payload.commits.map(c => { - return (c.modified.filter(s => { - robot.log.debug(JSON.stringify(s)) - return (s.search(settingPattern) >= 0) - })) - }).flat(2) - const changes = added.concat(modified) - const configs = changes.map(file => { - const matches = file.match(settingPattern) - robot.log.debug(`${JSON.stringify(file)} \n ${matches[1]}`) - return { name: matches[1] + '.yml', path: file } - }) - return configs + const pattern = Settings.SUB_ORG_PATTERN + + const getMatchingFiles = (commits, type) => + commits.flatMap((c) => c[type].filter((file) => pattern.test(file))) + + const changes = [ + ...getMatchingFiles(payload.commits, 'added'), + ...getMatchingFiles(payload.commits, 'modified') + ] + + return changes.map((file) => ({ + name: file.match(/([^/]+)\.yml$/)[1], + path: file + })) } function getAllChangedRepoConfigs (payload, owner) { - const settingPattern = Settings.REPO_PATTERN - // Changes will be an array of files that were added - const added = payload.commits.map(c => { - return (c.added.filter(s => { - robot.log.debug(JSON.stringify(s)) - return (s.search(settingPattern) >= 0) - })) - }).flat(2) - const modified = payload.commits.map(c => { - return (c.modified.filter(s => { - robot.log.debug(JSON.stringify(s)) - return (s.search(settingPattern) >= 0) - })) - }).flat(2) - const changes = added.concat(modified) - const configs = changes.map(file => { - robot.log.debug(`${JSON.stringify(file)}`) - return { repo: file.match(settingPattern)[1], owner } - }) - return configs - } + const pattern = Settings.REPO_PATTERN - function getChangedRepoConfigName (glob, files, owner) { - const modifiedFiles = files.filter(s => { - robot.log.debug(JSON.stringify(s)) - return (s.search(glob) >= 0) - }) + const getMatchingFiles = (commits, type) => + commits.flatMap((c) => c[type].filter((file) => pattern.test(file))) - return modifiedFiles.map(modifiedFile => { - return { repo: modifiedFile.match(glob)[1], owner } - }) + const changes = [ + ...getMatchingFiles(payload.commits, 'added'), + ...getMatchingFiles(payload.commits, 'modified') + ] + + return changes.map((file) => ({ + name: file.match(/([^/]+)\.yml$/)[1], + owner + })) } - function getChangedSubOrgConfigName (glob, files) { - const modifiedFiles = files.filter(s => { - robot.log.debug(JSON.stringify(s)) - return (s.search(glob) >= 0) - }) + function getChangedRepoConfigName (files, owner) { + const pattern = Settings.REPO_PATTERN - return modifiedFiles.map(modifiedFile => { - robot.log.debug(`${JSON.stringify(modifiedFile)}`) - return { name: modifiedFile.match(glob)[1] + '.yml', path: modifiedFile } - }) + const modifiedFiles = files.filter((s) => pattern.test(s)) + + return modifiedFiles.map((modifiedFile) => ({ + repo: modifiedFile.match(/([^/]+)\.yml$/)[1], + owner + })) } + function getChangedSubOrgConfigName (files) { + const pattern = Settings.SUB_ORG_PATTERN + + const modifiedFiles = files.filter((s) => pattern.test(s)) + + return modifiedFiles.map((modifiedFile) => ({ + name: modifiedFile.match(/([^/]+)\.yml$/)[1], + path: modifiedFile + })) + } async function createCheckRun (context, pull_request, head_sha, head_branch) { const { payload } = context // robot.log.debug(`Check suite was requested! for ${context.repo()} ${pull_request.number} ${head_sha} ${head_branch}`) @@ -604,14 +589,14 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) => return syncAllSettings(true, context, context.repo(), pull_request.head.ref) } - const repoChanges = getChangedRepoConfigName(Settings.REPO_PATTERN, files, context.repo().owner) + const repoChanges = getChangedRepoConfigName(files, context.repo().owner) if (repoChanges.length > 0) { return Promise.all(repoChanges.map(repo => { return syncSettings(true, context, repo, pull_request.head.ref) })) } - const subOrgChanges = getChangedSubOrgConfigName(Settings.SUB_ORG_PATTERN, files, context.repo().owner) + const subOrgChanges = getChangedSubOrgConfigName(files) if (subOrgChanges.length) { return Promise.all(subOrgChanges.map(suborg => { return syncSubOrgSettings(true, context, suborg, context.repo(), pull_request.head.ref) diff --git a/lib/glob.js b/lib/glob.js index 78a0eaaf..f78cee03 100644 --- a/lib/glob.js +++ b/lib/glob.js @@ -1,44 +1,14 @@ -class Glob { - constructor (glob) { - this.glob = glob - - // If not a glob pattern then just match the string. - if (!this.glob.includes('*')) { - this.regexp = new RegExp(`.*${this.glob}.*`, 'u') - return - } - this.regexptText = this.globize(this.glob) - this.regexp = new RegExp(`^${this.regexptText}$`, 'u') - } - - globize (glob) { - return glob - .replace(/\\/g, '\\\\') // escape backslashes - .replace(/\//g, '\\/') // escape forward slashes - .replace(/\./g, '\\.') // escape periods - .replace(/\?/g, '([^\\/])') // match any single character except / - .replace(/\*\*/g, '.+') // match any character except /, including / - .replace(/\*/g, '([^\\/]*)') // match any character except / - } - - toString () { - return this.glob - } +const { minimatch } = require('minimatch') - [Symbol.search] (s) { - return s.search(this.regexp) - } - - [Symbol.match] (s) { - return s.match(this.regexp) - } - - [Symbol.replace] (s, replacement) { - return s.replace(this.regexp, replacement) +class Glob { + constructor (pattern, options = {}) { + this.pattern = pattern + this.options = options } - [Symbol.replaceAll] (s, replacement) { - return s.replaceAll(this.regexp, replacement) + test (input) { + return minimatch(input, this.pattern, this.options) } } + module.exports = Glob diff --git a/lib/plugins/diffable.js b/lib/plugins/diffable.js index c5c5b253..cc732bf6 100644 --- a/lib/plugins/diffable.js +++ b/lib/plugins/diffable.js @@ -37,41 +37,22 @@ module.exports = class Diffable extends ErrorStash { filterEntries () { let filteredEntries = Array.from(this.entries) - // this.log.debug(` entries ${JSON.stringify(filteredEntries)}`) filteredEntries = filteredEntries.filter(attrs => { - if (Array.isArray(attrs.exclude)) { - const excludeGlobs = attrs.exclude.map(exc => new Glob(exc)); - const excludeGlobsMatch = excludeGlobs.some(glob => !!this.repo.repo.match(glob)); + if (!Array.isArray(attrs.exclude)) return true - if (!attrs.exclude.includes(this.repo.repo) && !excludeGlobsMatch) { - // this.log.debug(`returning not excluded entry = ${JSON.stringify(attrs)} for repo ${this.repo.repo}`) - return true - } else { - // this.log.debug(`skipping excluded entry = ${JSON.stringify(attrs)} for repo ${this.repo.repo}`) - return false - } - } else { - // this.log.debug(`No excludes. Returning unfiltered entries = ${JSON.stringify(attrs)} for repo ${this.repo.repo}`) - return true - } + const excludeGlobs = attrs.exclude.map(exc => new Glob(exc)) + const isExcluded = excludeGlobs.some(glob => glob.test(this.repo.repo)) + return !isExcluded }) + filteredEntries = filteredEntries.filter(attrs => { - if (Array.isArray(attrs.include)) { - const includeGlobs = attrs.include.map(inc => new Glob(inc)); - const includeGlobsMatch = includeGlobs.some(glob => !!this.repo.repo.match(glob)); + if (!Array.isArray(attrs.include)) return true - if (attrs.include.includes(this.repo.repo) || includeGlobsMatch) { - // this.log.debug(`returning included entry = ${JSON.stringify(attrs)} for repo ${this.repo.repo}`) - return true - } else { - // this.log.debug(`skipping not included entry = ${JSON.stringify(attrs)} for repo ${this.repo.repo}`) - return false - } - } else { - // this.log.debug(`No includes. Returning unfiltered entries = ${JSON.stringify(attrs)} for repo ${this.repo.repo}`) - return true - } + const includeGlobs = attrs.include.map(exc => new Glob(exc)) + const isIncluded = includeGlobs.some(glob => glob.test(this.repo.repo)) + return isIncluded }) + filteredEntries = filteredEntries.map(e => { const { exclude, include, ...o } = e return o diff --git a/lib/settings.js b/lib/settings.js index 56e20e88..172f808e 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -374,12 +374,12 @@ ${this.results.reduce((x, y) => { }) } - getSubOrgConfig(repoName) { + getSubOrgConfig (repoName) { if (this.subOrgConfigs) { - for (const k of Object.keys(this.subOrgConfigs)) { - const repoPattern = new Glob(k) - if (repoName.search(repoPattern) >= 0) { - return this.subOrgConfigs[k] + for (const pattern of Object.keys(this.subOrgConfigs)) { + const glob = new Glob(pattern) + if (glob.test(repoName)) { + return this.subOrgConfigs[pattern] } } } @@ -452,8 +452,7 @@ ${this.results.reduce((x, y) => { const restrictedRepos = this.config.restrictedRepos // Skip configuring any restricted repos if (Array.isArray(restrictedRepos)) { - // For backward compatibility support the old format - if (restrictedRepos.includes(repoName)) { + if (this.includesRepo(repoName, restrictedRepos)) { this.log.debug(`Skipping retricted repo ${repoName}`) return true } else { @@ -480,8 +479,8 @@ ${this.results.reduce((x, y) => { return false } - includesRepo(repoName, restrictedRepos) { - return restrictedRepos.filter((restrictedRepo) => { return RegExp(restrictedRepo).test(repoName) }).length > 0 + includesRepo (repoName, restrictedRepos) { + return restrictedRepos.map((restrictedRepo) => new Glob(restrictedRepo).test(repoName)).includes(true) } async eachRepositoryRepos (github, log) { diff --git a/package-lock.json b/package-lock.json index 8823564d..fd82d5c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "eta": "^3.5.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", + "minimatch": "^10.0.1", "node-cron": "^3.0.2", "octokit": "^4.1.2", "probot": "^13.4.4" @@ -629,6 +630,17 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", @@ -673,6 +685,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", @@ -696,6 +721,30 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -4189,7 +4238,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "license": "MIT" }, "node_modules/before-after-hook": { "version": "2.2.3", @@ -4256,13 +4305,12 @@ "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -4662,7 +4710,8 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/content-disposition": { "version": "0.5.4", @@ -5577,6 +5626,17 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -5598,6 +5658,19 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5636,6 +5709,32 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-n/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-n/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -5656,6 +5755,30 @@ "eslint": ">=5.16.0" } }, + "node_modules/eslint-plugin-node/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-node/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-node/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5713,6 +5836,17 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -5725,6 +5859,19 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -5842,6 +5989,17 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5897,6 +6055,19 @@ "node": ">=8" } }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6564,6 +6735,30 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -9588,15 +9783,18 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -9715,6 +9913,30 @@ "url": "https://opencollective.com/nodemon" } }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", @@ -9785,6 +10007,17 @@ "node": ">= 4" } }, + "node_modules/npm-run-all/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/npm-run-all/node_modules/cross-spawn": { "version": "6.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", @@ -9802,6 +10035,19 @@ "node": ">=4.8" } }, + "node_modules/npm-run-all/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/npm-run-all/node_modules/path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -11875,6 +12121,17 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/standard/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/standard/node_modules/eslint-plugin-n": { "version": "15.7.0", "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", @@ -11970,6 +12227,19 @@ "node": ">=10" } }, + "node_modules/standard/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -12219,6 +12489,30 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/package.json b/package.json index c1f2f5c5..1028c553 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "eta": "^3.5.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", + "minimatch": "^10.0.1", "node-cron": "^3.0.2", "octokit": "^4.1.2", "probot": "^13.4.4" diff --git a/test/unit/lib/glob.test.ts b/test/unit/lib/glob.test.ts deleted file mode 100644 index 27b6d29b..00000000 --- a/test/unit/lib/glob.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -const Glob = require('../../../lib/glob') - -describe('glob test', function () { - - test('Test Glob **', () => { - let pattern = new Glob('**/xss') - let str = 'test/web/xss' - expect(str.search(pattern)>=0).toBeTruthy() - str = 'test/web/xsssss' - expect(str.search(pattern)>=0).toBeFalsy() - - pattern = new Glob('**/*.txt') - str = 'sub/3.txt' - expect(str.search(pattern)>=0).toBeTruthy() - str = '/sub1/sub2/sub3/3.txt' - expect(str.search(pattern)>=0).toBeTruthy() - - pattern = new Glob('**/csrf-protection-disabled') - str = 'java/csrf-protection-disabled' - expect(str.search(pattern)>=0).toBeTruthy() - str = '/java/test/csrf-protection-disabled' - expect(str.search(pattern)>=0).toBeTruthy() - }) - - test('Test Glob *', () => { - let str = 'web/xss' - let pattern = new Glob('*/xss') - expect(str.search(pattern)>=0).toBeTruthy() - - pattern = new Glob('./[0-9].*') - str = './1.gif' - expect(str.search(pattern)>=0).toBeTruthy() - str = './2.gif' - expect(str.search(pattern)>=0).toBeTruthy() - str = './2.' - expect(str.search(pattern)>=0).toBeTruthy() - - pattern = new Glob('*/csrf-protection-disabled') - str = 'java/csrf-protection-disabled' - expect(str.search(pattern)>=0).toBeTruthy() - str = 'rb/csrf-protection-disabled' - expect(str.search(pattern)>=0).toBeTruthy() - - pattern = new Glob('*/hardcoded-credential*') - str = 'java/csrf-protection-disabled' - expect(str.search(pattern)>=0).toBeFalsy() - str = 'rb/csrf-protection-disabled' - expect(str.search(pattern)>=0).toBeFalsy() - str = 'cs/hardcoded-credentials' - expect(str.search(pattern)>=0).toBeTruthy() - str = 'java/hardcoded-credential-api-call' - expect(str.search(pattern)>=0).toBeTruthy() - - }) - - test('Test Glob no *', () => { - let pattern = new Glob('csrf-protection-disabled') - let str = 'java/hardcoded-credential-api-call' - expect(str.search(pattern)>=0).toBeFalsy() - str = 'cs/test/hardcoded-credentials' - expect(str.search(pattern)>=0).toBeFalsy() - str = 'rb/csrf-protection-disabled' - expect(str.search(pattern)>=0).toBeTruthy() - str = 'java/csrf-protection-disabled' - expect(str.search(pattern)>=0).toBeTruthy() - - pattern = new Glob('csrf') - str = 'java/hardcoded-credential-api-call' - expect(str.search(pattern)>=0).toBeFalsy() - str = 'cs/test/hardcoded-credentials' - expect(str.search(pattern)>=0).toBeFalsy() - str = 'rb/csrf-protection-disabled' - expect(str.search(pattern)>=0).toBeTruthy() - str = 'java/csrf-protection-disabled' - expect(str.search(pattern)>=0).toBeTruthy() - }) - -}) diff --git a/test/unit/lib/settings.test.js b/test/unit/lib/settings.test.js index 8b2ec618..c2f43ede 100644 --- a/test/unit/lib/settings.test.js +++ b/test/unit/lib/settings.test.js @@ -116,7 +116,7 @@ repository: beforeEach(() => { stubConfig = { restrictedRepos: { - exclude: ['foo', '.*-test$', '^personal-.*$'] + exclude: ['foo', '*-test', 'personal-*'] } } }) @@ -143,7 +143,7 @@ repository: beforeEach(() => { stubConfig = { restrictedRepos: { - include: ['foo', '.*-test$', '^personal-.*$'] + include: ['foo', '*-test', 'personal-*'] } } })