-
Notifications
You must be signed in to change notification settings - Fork 296
fix(SEC-005): add cross-repository allowlist validation to five handler files #19333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5553049
687c879
c678bb5
d628e24
8d0af6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,9 @@ | ||||||||||||||||||||||
| // @ts-check | ||||||||||||||||||||||
| /// <reference types="@actions/github-script" /> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const { validateTargetRepo, parseAllowedRepos, getDefaultTargetRepo } = require("./repo_helpers.cjs"); | ||||||||||||||||||||||
| const { ERR_VALIDATION } = require("./error_codes.cjs"); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Dynamic repository checkout utilities for multi-repo scenarios | ||||||||||||||||||||||
| * Enables switching between different repositories during handler execution | ||||||||||||||||||||||
|
|
@@ -51,6 +54,7 @@ async function getCurrentCheckoutRepo() { | |||||||||||||||||||||
| * @param {string} token - GitHub token for authentication | ||||||||||||||||||||||
| * @param {Object} options - Additional options | ||||||||||||||||||||||
| * @param {string} [options.baseBranch] - Base branch to checkout (defaults to 'main') | ||||||||||||||||||||||
| * @param {string[]|string} [options.allowedRepos] - Allowed repository patterns for allowlist validation | ||||||||||||||||||||||
| * @returns {Promise<Object>} Result with success status | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| async function checkoutRepo(repoSlug, token, options = {}) { | ||||||||||||||||||||||
|
|
@@ -60,10 +64,20 @@ async function checkoutRepo(repoSlug, token, options = {}) { | |||||||||||||||||||||
| if (parts.length !== 2 || !parts[0] || !parts[1]) { | ||||||||||||||||||||||
| return { | ||||||||||||||||||||||
| success: false, | ||||||||||||||||||||||
| error: `Invalid repository slug: ${repoSlug}. Expected format: owner/repo`, | ||||||||||||||||||||||
| error: `${ERR_VALIDATION}: Invalid repository slug: ${repoSlug}. Expected format: owner/repo`, | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Validate target repo against configured allowlist before any git operations | ||||||||||||||||||||||
| const allowedRepos = parseAllowedRepos(options.allowedRepos); | ||||||||||||||||||||||
| if (allowedRepos.size > 0) { | ||||||||||||||||||||||
| const defaultRepo = getDefaultTargetRepo(); | ||||||||||||||||||||||
| const validation = validateTargetRepo(repoSlug, defaultRepo, allowedRepos); | ||||||||||||||||||||||
| if (!validation.valid) { | ||||||||||||||||||||||
| return { success: false, error: `${ERR_VALIDATION}: ${validation.error}` }; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+73
to
+78
|
||||||||||||||||||||||
| if (allowedRepos.size > 0) { | |
| const defaultRepo = getDefaultTargetRepo(); | |
| const validation = validateTargetRepo(repoSlug, defaultRepo, allowedRepos); | |
| if (!validation.valid) { | |
| return { success: false, error: `${ERR_VALIDATION}: ${validation.error}` }; | |
| } | |
| const defaultRepo = getDefaultTargetRepo(); | |
| const validation = validateTargetRepo(repoSlug, defaultRepo, allowedRepos); | |
| if (!validation.valid) { | |
| return { success: false, error: `${ERR_VALIDATION}: ${validation.error}` }; |
Copilot
AI
Mar 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The allowlist check is currently gated by if (allowedRepos.size > 0), which means a cross-repo repoSlug will proceed with git operations when allowedRepos is unset/empty. For SEC-005, validation should still run with an empty allowlist so that only defaultRepo is permitted (and any other repo is rejected). Also note that createCheckoutManager().switchTo() calls checkoutRepo() without passing allowedRepos, so this validation path will never run unless callers supply it explicitly.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,8 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| // @ts-check | ||||||||||||||||||||||||||||||||||||||||||||||||
| /// <reference types="@actions/github-script" /> | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| const { validateTargetRepo, parseAllowedRepos, getDefaultTargetRepo } = require("./repo_helpers.cjs"); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||
| * @fileoverview Extra Empty Commit Helper | ||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -47,16 +49,29 @@ function isCrossRepoTarget(repoOwner, repoName) { | |||||||||||||||||||||||||||||||||||||||||||||||
| * @param {number} [options.newCommitCount] - Number of new commits being pushed. Only pushes the | ||||||||||||||||||||||||||||||||||||||||||||||||
| * empty commit when exactly 1 new commit was pushed, preventing accidental workflow-file | ||||||||||||||||||||||||||||||||||||||||||||||||
| * modifications on multi-commit branches and reducing loop risk. | ||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string[]|string} [options.allowedRepos] - Allowed repository patterns for allowlist validation | ||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns {Promise<{success: boolean, skipped?: boolean, error?: string}>} | ||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||
| async function pushExtraEmptyCommit({ branchName, repoOwner, repoName, commitMessage, newCommitCount }) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| async function pushExtraEmptyCommit({ branchName, repoOwner, repoName, commitMessage, newCommitCount, allowedRepos: allowedReposInput }) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| const token = process.env.GH_AW_CI_TRIGGER_TOKEN; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| if (!token || !token.trim()) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| core.info("No extra empty commit token configured - skipping"); | ||||||||||||||||||||||||||||||||||||||||||||||||
| return { success: true, skipped: true }; | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Validate target repository against allowlist before any git operations | ||||||||||||||||||||||||||||||||||||||||||||||||
| const allowedRepos = parseAllowedRepos(allowedReposInput); | ||||||||||||||||||||||||||||||||||||||||||||||||
| if (allowedRepos.size > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| const targetRepo = `${repoOwner}/${repoName}`; | ||||||||||||||||||||||||||||||||||||||||||||||||
| const defaultRepo = getDefaultTargetRepo(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| const validation = validateTargetRepo(targetRepo, defaultRepo, allowedRepos); | ||||||||||||||||||||||||||||||||||||||||||||||||
| if (!validation.valid) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| core.warning(`ERR_VALIDATION: ${validation.error}`); | ||||||||||||||||||||||||||||||||||||||||||||||||
| return { success: false, error: validation.error ?? "" }; | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+63
to
+73
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using |
||||||||||||||||||||||||||||||||||||||||||||||||
| // Cross-repo guard: never push an extra empty commit to a different repository. | ||||||||||||||||||||||||||||||||||||||||||||||||
| // A token is needed to create the PR and that will trigger events anyway. | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+65
to
76
|
||||||||||||||||||||||||||||||||||||||||||||||||
| if (allowedRepos.size > 0) { | |
| const targetRepo = `${repoOwner}/${repoName}`; | |
| const defaultRepo = getDefaultTargetRepo(); | |
| const validation = validateTargetRepo(targetRepo, defaultRepo, allowedRepos); | |
| if (!validation.valid) { | |
| core.warning(`ERR_VALIDATION: ${validation.error}`); | |
| return { success: false, error: validation.error ?? "" }; | |
| } | |
| } | |
| // Cross-repo guard: never push an extra empty commit to a different repository. | |
| // A token is needed to create the PR and that will trigger events anyway. | |
| const targetRepo = `${repoOwner}/${repoName}`; | |
| const defaultRepo = getDefaultTargetRepo(); | |
| const validation = validateTargetRepo(targetRepo, defaultRepo, allowedRepos); | |
| if (!validation.valid) { | |
| core.warning(`ERR_VALIDATION: ${validation.error}`); | |
| return { success: false, error: validation.error ?? "" }; | |
| } | |
| // Cross-repo guard: never push an extra empty commit to a different repository. | |
| // A token is needed to create the PR and that will trigger events anyway. | |
| // A token is needed to create the PR and that will trigger events anyway. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||
| const fs = require("fs"); | ||||||||||||||||||||||||||||||||||||||||||||||
| const path = require("path"); | ||||||||||||||||||||||||||||||||||||||||||||||
| const { execGitSync } = require("./git_helpers.cjs"); | ||||||||||||||||||||||||||||||||||||||||||||||
| const { validateTargetRepo, parseAllowedRepos, getDefaultTargetRepo } = require("./repo_helpers.cjs"); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
| * Debug logging helper - logs to stderr when DEBUG env var matches | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -129,9 +130,11 @@ function getRemoteOriginUrl(repoPath) { | |||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string} repoSlug - The repository slug to find (owner/repo format) | ||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string} [workspaceRoot] - The workspace root to search from | ||||||||||||||||||||||||||||||||||||||||||||||
| * @param {Object} [options] - Additional options | ||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string[]|string} [options.allowedRepos] - Allowed repository patterns for validation | ||||||||||||||||||||||||||||||||||||||||||||||
| * @returns {Object} Result with success status and path or error | ||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||
| function findRepoCheckout(repoSlug, workspaceRoot) { | ||||||||||||||||||||||||||||||||||||||||||||||
| function findRepoCheckout(repoSlug, workspaceRoot, options = {}) { | ||||||||||||||||||||||||||||||||||||||||||||||
| const ws = workspaceRoot || process.env.GITHUB_WORKSPACE || process.cwd(); | ||||||||||||||||||||||||||||||||||||||||||||||
| const targetSlug = normalizeRepoSlug(repoSlug); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -144,6 +147,16 @@ function findRepoCheckout(repoSlug, workspaceRoot) { | |||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Validate target repo against configured allowlist before searching | ||||||||||||||||||||||||||||||||||||||||||||||
| const allowedRepos = parseAllowedRepos(options.allowedRepos); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (allowedRepos.size > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||
| const defaultRepo = getDefaultTargetRepo(); | ||||||||||||||||||||||||||||||||||||||||||||||
| const validation = validateTargetRepo(targetSlug, defaultRepo, allowedRepos); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!validation.valid) { | ||||||||||||||||||||||||||||||||||||||||||||||
| return { success: false, error: validation.error }; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+150
to
+158
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Find all git directories in the workspace | ||||||||||||||||||||||||||||||||||||||||||||||
| const gitDirs = findGitDirectories(ws); | ||||||||||||||||||||||||||||||||||||||||||||||
| debugLog(`Found ${gitDirs.length} git directories: ${gitDirs.join(", ")}`); | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+152
to
162
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (allowedRepos.size > 0) { | |
| const defaultRepo = getDefaultTargetRepo(); | |
| const validation = validateTargetRepo(targetSlug, defaultRepo, allowedRepos); | |
| if (!validation.valid) { | |
| return { success: false, error: validation.error }; | |
| } | |
| } | |
| // Find all git directories in the workspace | |
| const gitDirs = findGitDirectories(ws); | |
| debugLog(`Found ${gitDirs.length} git directories: ${gitDirs.join(", ")}`); | |
| const defaultRepo = getDefaultTargetRepo(); | |
| const validation = validateTargetRepo(targetSlug, defaultRepo, allowedRepos); | |
| if (!validation.valid) { | |
| return { success: false, error: validation.error }; | |
| } | |
| // Find all git directories in the workspace | |
| const gitDirs = findGitDirectories(ws); | |
| debugLog(`Found ${gitDirs.length} git directories: ${gitDirs.join(", ")}`); | |
| const gitDirs = findGitDirectories(ws); | |
| debugLog(`Found ${gitDirs.length} git directories: ${gitDirs.join(", ")}`); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,8 @@ | ||||||||||||||||||||||||||||||||||
| // @ts-check | ||||||||||||||||||||||||||||||||||
| /// <reference types="@actions/github-script" /> | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const { validateTargetRepo, parseAllowedRepos, getDefaultTargetRepo } = require("./repo_helpers.cjs"); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * Get the base branch name, resolving dynamically based on event context. | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
|
|
@@ -40,6 +42,21 @@ async function getBaseBranch(targetRepo = null) { | |||||||||||||||||||||||||||||||||
| if (typeof github !== "undefined") { | ||||||||||||||||||||||||||||||||||
| const repoOwner = targetRepo?.owner ?? context.repo.owner; | ||||||||||||||||||||||||||||||||||
| const repoName = targetRepo?.repo ?? context.repo.repo; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Validate target repo against allowlist before any API calls | ||||||||||||||||||||||||||||||||||
| const targetRepoSlug = `${repoOwner}/${repoName}`; | ||||||||||||||||||||||||||||||||||
| const allowedRepos = parseAllowedRepos(process.env.GH_AW_ALLOWED_REPOS); | ||||||||||||||||||||||||||||||||||
| if (allowedRepos.size > 0) { | ||||||||||||||||||||||||||||||||||
| const defaultRepo = getDefaultTargetRepo(); | ||||||||||||||||||||||||||||||||||
| const validation = validateTargetRepo(targetRepoSlug, defaultRepo, allowedRepos); | ||||||||||||||||||||||||||||||||||
| if (!validation.valid) { | ||||||||||||||||||||||||||||||||||
| if (typeof core !== "undefined") { | ||||||||||||||||||||||||||||||||||
| core.warning(`ERR_VALIDATION: ${validation.error}`); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| return process.env.DEFAULT_BRANCH || "main"; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
+57
|
||||||||||||||||||||||||||||||||||
| if (allowedRepos.size > 0) { | |
| const defaultRepo = getDefaultTargetRepo(); | |
| const validation = validateTargetRepo(targetRepoSlug, defaultRepo, allowedRepos); | |
| if (!validation.valid) { | |
| if (typeof core !== "undefined") { | |
| core.warning(`ERR_VALIDATION: ${validation.error}`); | |
| } | |
| return process.env.DEFAULT_BRANCH || "main"; | |
| } | |
| const defaultRepo = getDefaultTargetRepo(); | |
| const validation = validateTargetRepo(targetRepoSlug, defaultRepo, allowedRepos); | |
| if (!validation.valid) { | |
| if (typeof core !== "undefined") { | |
| core.warning(`ERR_VALIDATION: ${validation.error}`); | |
| } | |
| return process.env.DEFAULT_BRANCH || "main"; |
Copilot
AI
Mar 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This introduces allowlist validation behavior for cross-repo base-branch lookups, but get_base_branch.test.cjs doesn’t currently cover the allow/deny outcomes. Adding tests that set GH_AW_ALLOWED_REPOS and verify the function warns/short-circuits on a disallowed targetRepo would help keep SEC-005 covered.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // @ts-check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { getErrorMessage } = require("./error_helpers.cjs"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { validateTargetRepo, parseAllowedRepos, getDefaultTargetRepo } = require("./repo_helpers.cjs"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fs = require("fs"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -100,6 +101,15 @@ function registerPredefinedTools(server, tools, config, registerTool, normalizeT | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (tool.name === "create_pull_request" && config.create_pull_request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const targetRepo = config.create_pull_request["target-repo"]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (targetRepo) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Validate the configured target-repo against the allowed-repos list | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const allowedRepos = parseAllowedRepos(config.create_pull_request.allowed_repos); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (allowedRepos.size > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const defaultRepo = getDefaultTargetRepo(config.create_pull_request); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const validation = validateTargetRepo(targetRepo, defaultRepo, allowedRepos); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!validation.valid) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| server.debug(`WARNING: SEC-005: ${validation.error}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+104
to
+112
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toolToRegister = JSON.parse(JSON.stringify(tool)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toolToRegister.description += ` Note: This workflow is configured to create pull requests in '${targetRepo}'. You do not need to specify the repo parameter.`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+106
to
114
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (allowedRepos.size > 0) { | |
| const defaultRepo = getDefaultTargetRepo(config.create_pull_request); | |
| const validation = validateTargetRepo(targetRepo, defaultRepo, allowedRepos); | |
| if (!validation.valid) { | |
| server.debug(`WARNING: SEC-005: ${validation.error}`); | |
| } | |
| } | |
| toolToRegister = JSON.parse(JSON.stringify(tool)); | |
| toolToRegister.description += ` Note: This workflow is configured to create pull requests in '${targetRepo}'. You do not need to specify the repo parameter.`; | |
| let repoNote = ` Note: This workflow is configured to create pull requests in '${targetRepo}'. You do not need to specify the repo parameter.`; | |
| if (allowedRepos.size > 0) { | |
| const defaultRepo = getDefaultTargetRepo(config.create_pull_request); | |
| const validation = validateTargetRepo(targetRepo, defaultRepo, allowedRepos); | |
| if (!validation.valid) { | |
| server.debug(`WARNING: SEC-005: ${validation.error}`); | |
| // Surface potential misconfiguration in the tool description as well | |
| repoNote += ` WARNING: The configured target-repo may not be in the allowed list and this configuration might be ignored at runtime.`; | |
| } | |
| } else { | |
| // No allowed_repos configured: surface this as a warning so misconfigurations are easier to detect | |
| server.debug( | |
| `WARNING: SEC-005: No allowed_repos are configured for create_pull_request; skipping validation for configured target-repo '${targetRepo}'.` | |
| ); | |
| repoNote += ` WARNING: No allowed_repos are configured for this workflow, so this default may not be enforced.`; | |
| } | |
| toolToRegister = JSON.parse(JSON.stringify(tool)); | |
| toolToRegister.description += repoNote; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good use of
ERR_VALIDATIONprefix for structured error reporting — this makes it easier to grep/filter validation errors in logs. 👍