From 2d0b9e69720bdc97ab423bdc3741e7096455ebae Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 17 Sep 2025 15:50:33 +0100 Subject: [PATCH] recompile improvers --- .github/workflows/ask.lock.yml | 378 ++++++++++++------ .github/workflows/ci-doctor.lock.yml | 378 ++++++++++++------ .../workflows/daily-backlog-burner.lock.yml | 378 ++++++++++++------ .../workflows/daily-perf-improver.lock.yml | 378 ++++++++++++------ .../workflows/daily-test-improver.lock.yml | 378 ++++++++++++------ .github/workflows/pr-fix.lock.yml | 378 ++++++++++++------ 6 files changed, 1554 insertions(+), 714 deletions(-) diff --git a/.github/workflows/ask.lock.yml b/.github/workflows/ask.lock.yml index 6c76f0b5d..9ecefea5a 100644 --- a/.github/workflows/ask.lock.yml +++ b/.github/workflows/ask.lock.yml @@ -2,7 +2,7 @@ # To update this file, edit the corresponding .md file and run: # gh aw compile # -# Effective stop-time: 2025-09-19 12:48:19 +# Effective stop-time: 2025-09-19 14:50:09 name: "Question Answering Researcher" on: @@ -594,7 +594,7 @@ jobs: main(); - name: Setup Safe Outputs Collector MCP env: - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true}}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{}}" run: | mkdir -p /tmp/safe-outputs cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF' @@ -899,8 +899,14 @@ jobs: description: "Push changes to a pull request branch", inputSchema: { type: "object", + required: ["branch_name", "message"], properties: { - message: { type: "string", description: "Optional commit message" }, + branch_name: { + type: "string", + description: + "The name of the branch to push to, should be the branch name associated with the pull request", + }, + message: { type: "string", description: "Commit message" }, pull_request_number: { type: ["number", "string"], description: "Optional pull request number for target '*'", @@ -1028,7 +1034,7 @@ jobs: - name: Setup MCPs env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true}}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{}}" run: | mkdir -p /tmp/mcp-config cat > /tmp/mcp-config/mcp-servers.json << 'EOF' @@ -1066,7 +1072,7 @@ jobs: WORKFLOW_NAME="Question Answering Researcher" # Check stop-time limit - STOP_TIME="2025-09-19 12:48:19" + STOP_TIME="2025-09-19 14:50:09" echo "Checking stop-time limit: $STOP_TIME" # Convert stop time to epoch seconds @@ -1327,7 +1333,7 @@ jobs: uses: actions/github-script@v8 env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true}}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{}}" with: script: | async function main() { @@ -1575,6 +1581,149 @@ jobs: repaired = repaired.replace(/,(\s*[}\]])/g, "$1"); return repaired; } + /** + * Validates that a value is a positive integer + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validatePositiveInteger(value, fieldName, lineNum) { + if (value === undefined || value === null) { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} is required`, + }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number or string field`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${value})`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'line' must be a positive integer`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an optional positive integer field + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validateOptionalPositiveInteger(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for specific field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a number or string`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a number or string`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a positive integer`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${value})`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an issue or pull request number (optional field) + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string}} Validation result + */ + function validateIssueOrPRNumber(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + return { isValid: true }; + } /** * Attempts to parse JSON with repair fallback * @param {string} jsonStr - The JSON string to parse @@ -1711,6 +1860,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const issueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-comment 'issue_number'", + i + 1 + ); + if (!issueNumValidation.isValid) { + errors.push(issueNumValidation.error); + continue; + } // Sanitize text content item.body = sanitizeContent(item.body); break; @@ -1759,6 +1918,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const labelsIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-labels 'issue_number'", + i + 1 + ); + if (!labelsIssueNumValidation.isValid) { + errors.push(labelsIssueNumValidation.error); + continue; + } // Sanitize label strings item.labels = item.labels.map( /** @param {any} label */ label => sanitizeContent(label) @@ -1809,40 +1978,43 @@ jobs: item.body = sanitizeContent(item.body); } // Validate issue_number if provided (for target "*") - if (item.issue_number !== undefined) { - if ( - typeof item.issue_number !== "number" && - typeof item.issue_number !== "string" - ) { - errors.push( - `Line ${i + 1}: update-issue 'issue_number' must be a number or string` - ); - continue; - } + const updateIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "update-issue 'issue_number'", + i + 1 + ); + if (!updateIssueNumValidation.isValid) { + errors.push(updateIssueNumValidation.error); + continue; } break; case "push-to-pr-branch": - // Validate message if provided (optional) - if (item.message !== undefined) { - if (typeof item.message !== "string") { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'message' must be a string` - ); - continue; - } - item.message = sanitizeContent(item.message); + // Validate required branch_name field + if (!item.branch_name || typeof item.branch_name !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'branch_name' string field` + ); + continue; } + // Validate required message field + if (!item.message || typeof item.message !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'message' string field` + ); + continue; + } + // Sanitize text content + item.branch_name = sanitizeContent(item.branch_name); + item.message = sanitizeContent(item.message); // Validate pull_request_number if provided (for target "*") - if (item.pull_request_number !== undefined) { - if ( - typeof item.pull_request_number !== "number" && - typeof item.pull_request_number !== "string" - ) { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'pull_request_number' must be a number or string` - ); - continue; - } + const pushPRNumValidation = validateIssueOrPRNumber( + item.pull_request_number, + "push-to-pr-branch 'pull_request_number'", + i + 1 + ); + if (!pushPRNumValidation.isValid) { + errors.push(pushPRNumValidation.error); + continue; } break; case "create-pull-request-review-comment": @@ -1854,28 +2026,17 @@ jobs: continue; } // Validate required line field - if ( - item.line === undefined || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment requires a 'line' number or string field` - ); - continue; - } - // Validate line is a positive integer - const lineNumber = - typeof item.line === "string" ? parseInt(item.line, 10) : item.line; - if ( - isNaN(lineNumber) || - lineNumber <= 0 || - !Number.isInteger(lineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'line' must be a positive integer` - ); + const lineValidation = validatePositiveInteger( + item.line, + "create-pull-request-review-comment 'line'", + i + 1 + ); + if (!lineValidation.isValid) { + errors.push(lineValidation.error); continue; } + // lineValidation.normalizedValue is guaranteed to be defined when isValid is true + const lineNumber = lineValidation.normalizedValue; // Validate required body field if (!item.body || typeof item.body !== "string") { errors.push( @@ -1886,36 +2047,24 @@ jobs: // Sanitize required text content item.body = sanitizeContent(item.body); // Validate optional start_line field - if (item.start_line !== undefined) { - if ( - typeof item.start_line !== "number" && - typeof item.start_line !== "string" - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a number or string` - ); - continue; - } - const startLineNumber = - typeof item.start_line === "string" - ? parseInt(item.start_line, 10) - : item.start_line; - if ( - isNaN(startLineNumber) || - startLineNumber <= 0 || - !Number.isInteger(startLineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a positive integer` - ); - continue; - } - if (startLineNumber > lineNumber) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` - ); - continue; - } + const startLineValidation = validateOptionalPositiveInteger( + item.start_line, + "create-pull-request-review-comment 'start_line'", + i + 1 + ); + if (!startLineValidation.isValid) { + errors.push(startLineValidation.error); + continue; + } + if ( + startLineValidation.normalizedValue !== undefined && + lineNumber !== undefined && + startLineValidation.normalizedValue > lineNumber + ) { + errors.push( + `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` + ); + continue; } // Validate optional side field if (item.side !== undefined) { @@ -1943,6 +2092,16 @@ jobs: ); continue; } + // Validate optional category field + if (item.category !== undefined) { + if (typeof item.category !== "string") { + errors.push( + `Line ${i + 1}: create-discussion 'category' must be a string` + ); + continue; + } + item.category = sanitizeContent(item.category); + } // Sanitize text content item.title = sanitizeContent(item.title); item.body = sanitizeContent(item.body); @@ -1984,22 +2143,13 @@ jobs: ); continue; } - if ( - item.line === undefined || - item.line === null || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert requires a 'line' field (number or string)` - ); - continue; - } - // Additional validation: line must be parseable as a positive integer - const parsedLine = parseInt(item.line, 10); - if (isNaN(parsedLine) || parsedLine <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${item.line})` - ); + const alertLineValidation = validatePositiveInteger( + item.line, + "create-code-scanning-alert 'line'", + i + 1 + ); + if (!alertLineValidation.isValid) { + errors.push(alertLineValidation.error); continue; } if (!item.severity || typeof item.severity !== "string") { @@ -2023,24 +2173,14 @@ jobs: continue; } // Validate optional column field - if (item.column !== undefined) { - if ( - typeof item.column !== "number" && - typeof item.column !== "string" - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a number or string` - ); - continue; - } - // Additional validation: must be parseable as a positive integer - const parsedColumn = parseInt(item.column, 10); - if (isNaN(parsedColumn) || parsedColumn <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${item.column})` - ); - continue; - } + const columnValidation = validateOptionalPositiveInteger( + item.column, + "create-code-scanning-alert 'column'", + i + 1 + ); + if (!columnValidation.isValid) { + errors.push(columnValidation.error); + continue; } // Validate optional ruleIdSuffix field if (item.ruleIdSuffix !== undefined) { diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 31c36d35e..325caeda3 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -2,7 +2,7 @@ # To update this file, edit the corresponding .md file and run: # gh aw compile # -# Effective stop-time: 2025-09-19 12:48:19 +# Effective stop-time: 2025-09-19 14:50:09 name: "CI Failure Doctor" "on": @@ -75,7 +75,7 @@ jobs: main(); - name: Setup Safe Outputs Collector MCP env: - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{},\"create-issue\":{}}" run: | mkdir -p /tmp/safe-outputs cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF' @@ -380,8 +380,14 @@ jobs: description: "Push changes to a pull request branch", inputSchema: { type: "object", + required: ["branch_name", "message"], properties: { - message: { type: "string", description: "Optional commit message" }, + branch_name: { + type: "string", + description: + "The name of the branch to push to, should be the branch name associated with the pull request", + }, + message: { type: "string", description: "Commit message" }, pull_request_number: { type: ["number", "string"], description: "Optional pull request number for target '*'", @@ -509,7 +515,7 @@ jobs: - name: Setup MCPs env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{},\"create-issue\":{}}" run: | mkdir -p /tmp/mcp-config cat > /tmp/mcp-config/mcp-servers.json << 'EOF' @@ -547,7 +553,7 @@ jobs: WORKFLOW_NAME="CI Failure Doctor" # Check stop-time limit - STOP_TIME="2025-09-19 12:48:19" + STOP_TIME="2025-09-19 14:50:09" echo "Checking stop-time limit: $STOP_TIME" # Convert stop time to epoch seconds @@ -941,7 +947,7 @@ jobs: uses: actions/github-script@v8 env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{},\"create-issue\":{}}" with: script: | async function main() { @@ -1189,6 +1195,149 @@ jobs: repaired = repaired.replace(/,(\s*[}\]])/g, "$1"); return repaired; } + /** + * Validates that a value is a positive integer + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validatePositiveInteger(value, fieldName, lineNum) { + if (value === undefined || value === null) { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} is required`, + }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number or string field`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${value})`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'line' must be a positive integer`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an optional positive integer field + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validateOptionalPositiveInteger(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for specific field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a number or string`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a number or string`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a positive integer`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${value})`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an issue or pull request number (optional field) + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string}} Validation result + */ + function validateIssueOrPRNumber(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + return { isValid: true }; + } /** * Attempts to parse JSON with repair fallback * @param {string} jsonStr - The JSON string to parse @@ -1325,6 +1474,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const issueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-comment 'issue_number'", + i + 1 + ); + if (!issueNumValidation.isValid) { + errors.push(issueNumValidation.error); + continue; + } // Sanitize text content item.body = sanitizeContent(item.body); break; @@ -1373,6 +1532,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const labelsIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-labels 'issue_number'", + i + 1 + ); + if (!labelsIssueNumValidation.isValid) { + errors.push(labelsIssueNumValidation.error); + continue; + } // Sanitize label strings item.labels = item.labels.map( /** @param {any} label */ label => sanitizeContent(label) @@ -1423,40 +1592,43 @@ jobs: item.body = sanitizeContent(item.body); } // Validate issue_number if provided (for target "*") - if (item.issue_number !== undefined) { - if ( - typeof item.issue_number !== "number" && - typeof item.issue_number !== "string" - ) { - errors.push( - `Line ${i + 1}: update-issue 'issue_number' must be a number or string` - ); - continue; - } + const updateIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "update-issue 'issue_number'", + i + 1 + ); + if (!updateIssueNumValidation.isValid) { + errors.push(updateIssueNumValidation.error); + continue; } break; case "push-to-pr-branch": - // Validate message if provided (optional) - if (item.message !== undefined) { - if (typeof item.message !== "string") { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'message' must be a string` - ); - continue; - } - item.message = sanitizeContent(item.message); + // Validate required branch_name field + if (!item.branch_name || typeof item.branch_name !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'branch_name' string field` + ); + continue; } + // Validate required message field + if (!item.message || typeof item.message !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'message' string field` + ); + continue; + } + // Sanitize text content + item.branch_name = sanitizeContent(item.branch_name); + item.message = sanitizeContent(item.message); // Validate pull_request_number if provided (for target "*") - if (item.pull_request_number !== undefined) { - if ( - typeof item.pull_request_number !== "number" && - typeof item.pull_request_number !== "string" - ) { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'pull_request_number' must be a number or string` - ); - continue; - } + const pushPRNumValidation = validateIssueOrPRNumber( + item.pull_request_number, + "push-to-pr-branch 'pull_request_number'", + i + 1 + ); + if (!pushPRNumValidation.isValid) { + errors.push(pushPRNumValidation.error); + continue; } break; case "create-pull-request-review-comment": @@ -1468,28 +1640,17 @@ jobs: continue; } // Validate required line field - if ( - item.line === undefined || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment requires a 'line' number or string field` - ); - continue; - } - // Validate line is a positive integer - const lineNumber = - typeof item.line === "string" ? parseInt(item.line, 10) : item.line; - if ( - isNaN(lineNumber) || - lineNumber <= 0 || - !Number.isInteger(lineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'line' must be a positive integer` - ); + const lineValidation = validatePositiveInteger( + item.line, + "create-pull-request-review-comment 'line'", + i + 1 + ); + if (!lineValidation.isValid) { + errors.push(lineValidation.error); continue; } + // lineValidation.normalizedValue is guaranteed to be defined when isValid is true + const lineNumber = lineValidation.normalizedValue; // Validate required body field if (!item.body || typeof item.body !== "string") { errors.push( @@ -1500,36 +1661,24 @@ jobs: // Sanitize required text content item.body = sanitizeContent(item.body); // Validate optional start_line field - if (item.start_line !== undefined) { - if ( - typeof item.start_line !== "number" && - typeof item.start_line !== "string" - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a number or string` - ); - continue; - } - const startLineNumber = - typeof item.start_line === "string" - ? parseInt(item.start_line, 10) - : item.start_line; - if ( - isNaN(startLineNumber) || - startLineNumber <= 0 || - !Number.isInteger(startLineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a positive integer` - ); - continue; - } - if (startLineNumber > lineNumber) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` - ); - continue; - } + const startLineValidation = validateOptionalPositiveInteger( + item.start_line, + "create-pull-request-review-comment 'start_line'", + i + 1 + ); + if (!startLineValidation.isValid) { + errors.push(startLineValidation.error); + continue; + } + if ( + startLineValidation.normalizedValue !== undefined && + lineNumber !== undefined && + startLineValidation.normalizedValue > lineNumber + ) { + errors.push( + `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` + ); + continue; } // Validate optional side field if (item.side !== undefined) { @@ -1557,6 +1706,16 @@ jobs: ); continue; } + // Validate optional category field + if (item.category !== undefined) { + if (typeof item.category !== "string") { + errors.push( + `Line ${i + 1}: create-discussion 'category' must be a string` + ); + continue; + } + item.category = sanitizeContent(item.category); + } // Sanitize text content item.title = sanitizeContent(item.title); item.body = sanitizeContent(item.body); @@ -1598,22 +1757,13 @@ jobs: ); continue; } - if ( - item.line === undefined || - item.line === null || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert requires a 'line' field (number or string)` - ); - continue; - } - // Additional validation: line must be parseable as a positive integer - const parsedLine = parseInt(item.line, 10); - if (isNaN(parsedLine) || parsedLine <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${item.line})` - ); + const alertLineValidation = validatePositiveInteger( + item.line, + "create-code-scanning-alert 'line'", + i + 1 + ); + if (!alertLineValidation.isValid) { + errors.push(alertLineValidation.error); continue; } if (!item.severity || typeof item.severity !== "string") { @@ -1637,24 +1787,14 @@ jobs: continue; } // Validate optional column field - if (item.column !== undefined) { - if ( - typeof item.column !== "number" && - typeof item.column !== "string" - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a number or string` - ); - continue; - } - // Additional validation: must be parseable as a positive integer - const parsedColumn = parseInt(item.column, 10); - if (isNaN(parsedColumn) || parsedColumn <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${item.column})` - ); - continue; - } + const columnValidation = validateOptionalPositiveInteger( + item.column, + "create-code-scanning-alert 'column'", + i + 1 + ); + if (!columnValidation.isValid) { + errors.push(columnValidation.error); + continue; } // Validate optional ruleIdSuffix field if (item.ruleIdSuffix !== undefined) { diff --git a/.github/workflows/daily-backlog-burner.lock.yml b/.github/workflows/daily-backlog-burner.lock.yml index 18a236738..6ef826d57 100644 --- a/.github/workflows/daily-backlog-burner.lock.yml +++ b/.github/workflows/daily-backlog-burner.lock.yml @@ -2,7 +2,7 @@ # To update this file, edit the corresponding .md file and run: # gh aw compile # -# Effective stop-time: 2025-09-19 12:48:20 +# Effective stop-time: 2025-09-19 14:50:09 name: "Daily Backlog Burner" "on": @@ -55,7 +55,7 @@ jobs: main(); - name: Setup Safe Outputs Collector MCP env: - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"target\":\"*\"},\"create-issue\":{},\"create-pull-request\":{}}" run: | mkdir -p /tmp/safe-outputs cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF' @@ -360,8 +360,14 @@ jobs: description: "Push changes to a pull request branch", inputSchema: { type: "object", + required: ["branch_name", "message"], properties: { - message: { type: "string", description: "Optional commit message" }, + branch_name: { + type: "string", + description: + "The name of the branch to push to, should be the branch name associated with the pull request", + }, + message: { type: "string", description: "Commit message" }, pull_request_number: { type: ["number", "string"], description: "Optional pull request number for target '*'", @@ -489,7 +495,7 @@ jobs: - name: Setup MCPs env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"target\":\"*\"},\"create-issue\":{},\"create-pull-request\":{}}" run: | mkdir -p /tmp/mcp-config cat > /tmp/mcp-config/mcp-servers.json << 'EOF' @@ -527,7 +533,7 @@ jobs: WORKFLOW_NAME="Daily Backlog Burner" # Check stop-time limit - STOP_TIME="2025-09-19 12:48:20" + STOP_TIME="2025-09-19 14:50:09" echo "Checking stop-time limit: $STOP_TIME" # Convert stop time to epoch seconds @@ -854,7 +860,7 @@ jobs: uses: actions/github-script@v8 env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"target\":\"*\"},\"create-issue\":{},\"create-pull-request\":{}}" with: script: | async function main() { @@ -1102,6 +1108,149 @@ jobs: repaired = repaired.replace(/,(\s*[}\]])/g, "$1"); return repaired; } + /** + * Validates that a value is a positive integer + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validatePositiveInteger(value, fieldName, lineNum) { + if (value === undefined || value === null) { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} is required`, + }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number or string field`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${value})`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'line' must be a positive integer`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an optional positive integer field + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validateOptionalPositiveInteger(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for specific field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a number or string`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a number or string`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a positive integer`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${value})`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an issue or pull request number (optional field) + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string}} Validation result + */ + function validateIssueOrPRNumber(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + return { isValid: true }; + } /** * Attempts to parse JSON with repair fallback * @param {string} jsonStr - The JSON string to parse @@ -1238,6 +1387,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const issueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-comment 'issue_number'", + i + 1 + ); + if (!issueNumValidation.isValid) { + errors.push(issueNumValidation.error); + continue; + } // Sanitize text content item.body = sanitizeContent(item.body); break; @@ -1286,6 +1445,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const labelsIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-labels 'issue_number'", + i + 1 + ); + if (!labelsIssueNumValidation.isValid) { + errors.push(labelsIssueNumValidation.error); + continue; + } // Sanitize label strings item.labels = item.labels.map( /** @param {any} label */ label => sanitizeContent(label) @@ -1336,40 +1505,43 @@ jobs: item.body = sanitizeContent(item.body); } // Validate issue_number if provided (for target "*") - if (item.issue_number !== undefined) { - if ( - typeof item.issue_number !== "number" && - typeof item.issue_number !== "string" - ) { - errors.push( - `Line ${i + 1}: update-issue 'issue_number' must be a number or string` - ); - continue; - } + const updateIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "update-issue 'issue_number'", + i + 1 + ); + if (!updateIssueNumValidation.isValid) { + errors.push(updateIssueNumValidation.error); + continue; } break; case "push-to-pr-branch": - // Validate message if provided (optional) - if (item.message !== undefined) { - if (typeof item.message !== "string") { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'message' must be a string` - ); - continue; - } - item.message = sanitizeContent(item.message); + // Validate required branch_name field + if (!item.branch_name || typeof item.branch_name !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'branch_name' string field` + ); + continue; } + // Validate required message field + if (!item.message || typeof item.message !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'message' string field` + ); + continue; + } + // Sanitize text content + item.branch_name = sanitizeContent(item.branch_name); + item.message = sanitizeContent(item.message); // Validate pull_request_number if provided (for target "*") - if (item.pull_request_number !== undefined) { - if ( - typeof item.pull_request_number !== "number" && - typeof item.pull_request_number !== "string" - ) { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'pull_request_number' must be a number or string` - ); - continue; - } + const pushPRNumValidation = validateIssueOrPRNumber( + item.pull_request_number, + "push-to-pr-branch 'pull_request_number'", + i + 1 + ); + if (!pushPRNumValidation.isValid) { + errors.push(pushPRNumValidation.error); + continue; } break; case "create-pull-request-review-comment": @@ -1381,28 +1553,17 @@ jobs: continue; } // Validate required line field - if ( - item.line === undefined || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment requires a 'line' number or string field` - ); - continue; - } - // Validate line is a positive integer - const lineNumber = - typeof item.line === "string" ? parseInt(item.line, 10) : item.line; - if ( - isNaN(lineNumber) || - lineNumber <= 0 || - !Number.isInteger(lineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'line' must be a positive integer` - ); + const lineValidation = validatePositiveInteger( + item.line, + "create-pull-request-review-comment 'line'", + i + 1 + ); + if (!lineValidation.isValid) { + errors.push(lineValidation.error); continue; } + // lineValidation.normalizedValue is guaranteed to be defined when isValid is true + const lineNumber = lineValidation.normalizedValue; // Validate required body field if (!item.body || typeof item.body !== "string") { errors.push( @@ -1413,36 +1574,24 @@ jobs: // Sanitize required text content item.body = sanitizeContent(item.body); // Validate optional start_line field - if (item.start_line !== undefined) { - if ( - typeof item.start_line !== "number" && - typeof item.start_line !== "string" - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a number or string` - ); - continue; - } - const startLineNumber = - typeof item.start_line === "string" - ? parseInt(item.start_line, 10) - : item.start_line; - if ( - isNaN(startLineNumber) || - startLineNumber <= 0 || - !Number.isInteger(startLineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a positive integer` - ); - continue; - } - if (startLineNumber > lineNumber) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` - ); - continue; - } + const startLineValidation = validateOptionalPositiveInteger( + item.start_line, + "create-pull-request-review-comment 'start_line'", + i + 1 + ); + if (!startLineValidation.isValid) { + errors.push(startLineValidation.error); + continue; + } + if ( + startLineValidation.normalizedValue !== undefined && + lineNumber !== undefined && + startLineValidation.normalizedValue > lineNumber + ) { + errors.push( + `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` + ); + continue; } // Validate optional side field if (item.side !== undefined) { @@ -1470,6 +1619,16 @@ jobs: ); continue; } + // Validate optional category field + if (item.category !== undefined) { + if (typeof item.category !== "string") { + errors.push( + `Line ${i + 1}: create-discussion 'category' must be a string` + ); + continue; + } + item.category = sanitizeContent(item.category); + } // Sanitize text content item.title = sanitizeContent(item.title); item.body = sanitizeContent(item.body); @@ -1511,22 +1670,13 @@ jobs: ); continue; } - if ( - item.line === undefined || - item.line === null || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert requires a 'line' field (number or string)` - ); - continue; - } - // Additional validation: line must be parseable as a positive integer - const parsedLine = parseInt(item.line, 10); - if (isNaN(parsedLine) || parsedLine <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${item.line})` - ); + const alertLineValidation = validatePositiveInteger( + item.line, + "create-code-scanning-alert 'line'", + i + 1 + ); + if (!alertLineValidation.isValid) { + errors.push(alertLineValidation.error); continue; } if (!item.severity || typeof item.severity !== "string") { @@ -1550,24 +1700,14 @@ jobs: continue; } // Validate optional column field - if (item.column !== undefined) { - if ( - typeof item.column !== "number" && - typeof item.column !== "string" - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a number or string` - ); - continue; - } - // Additional validation: must be parseable as a positive integer - const parsedColumn = parseInt(item.column, 10); - if (isNaN(parsedColumn) || parsedColumn <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${item.column})` - ); - continue; - } + const columnValidation = validateOptionalPositiveInteger( + item.column, + "create-code-scanning-alert 'column'", + i + 1 + ); + if (!columnValidation.isValid) { + errors.push(columnValidation.error); + continue; } // Validate optional ruleIdSuffix field if (item.ruleIdSuffix !== undefined) { diff --git a/.github/workflows/daily-perf-improver.lock.yml b/.github/workflows/daily-perf-improver.lock.yml index e56dd53d4..66d44167c 100644 --- a/.github/workflows/daily-perf-improver.lock.yml +++ b/.github/workflows/daily-perf-improver.lock.yml @@ -2,7 +2,7 @@ # To update this file, edit the corresponding .md file and run: # gh aw compile # -# Effective stop-time: 2025-09-19 12:48:20 +# Effective stop-time: 2025-09-19 14:50:09 name: "Daily Perf Improver" "on": @@ -69,7 +69,7 @@ jobs: main(); - name: Setup Safe Outputs Collector MCP env: - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"target\":\"*\"},\"create-issue\":{},\"create-pull-request\":{}}" run: | mkdir -p /tmp/safe-outputs cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF' @@ -374,8 +374,14 @@ jobs: description: "Push changes to a pull request branch", inputSchema: { type: "object", + required: ["branch_name", "message"], properties: { - message: { type: "string", description: "Optional commit message" }, + branch_name: { + type: "string", + description: + "The name of the branch to push to, should be the branch name associated with the pull request", + }, + message: { type: "string", description: "Commit message" }, pull_request_number: { type: ["number", "string"], description: "Optional pull request number for target '*'", @@ -503,7 +509,7 @@ jobs: - name: Setup MCPs env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"target\":\"*\"},\"create-issue\":{},\"create-pull-request\":{}}" run: | mkdir -p /tmp/mcp-config cat > /tmp/mcp-config/mcp-servers.json << 'EOF' @@ -541,7 +547,7 @@ jobs: WORKFLOW_NAME="Daily Perf Improver" # Check stop-time limit - STOP_TIME="2025-09-19 12:48:20" + STOP_TIME="2025-09-19 14:50:09" echo "Checking stop-time limit: $STOP_TIME" # Convert stop time to epoch seconds @@ -929,7 +935,7 @@ jobs: uses: actions/github-script@v8 env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"target\":\"*\"},\"create-issue\":{},\"create-pull-request\":{}}" with: script: | async function main() { @@ -1177,6 +1183,149 @@ jobs: repaired = repaired.replace(/,(\s*[}\]])/g, "$1"); return repaired; } + /** + * Validates that a value is a positive integer + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validatePositiveInteger(value, fieldName, lineNum) { + if (value === undefined || value === null) { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} is required`, + }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number or string field`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${value})`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'line' must be a positive integer`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an optional positive integer field + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validateOptionalPositiveInteger(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for specific field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a number or string`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a number or string`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a positive integer`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${value})`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an issue or pull request number (optional field) + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string}} Validation result + */ + function validateIssueOrPRNumber(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + return { isValid: true }; + } /** * Attempts to parse JSON with repair fallback * @param {string} jsonStr - The JSON string to parse @@ -1313,6 +1462,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const issueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-comment 'issue_number'", + i + 1 + ); + if (!issueNumValidation.isValid) { + errors.push(issueNumValidation.error); + continue; + } // Sanitize text content item.body = sanitizeContent(item.body); break; @@ -1361,6 +1520,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const labelsIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-labels 'issue_number'", + i + 1 + ); + if (!labelsIssueNumValidation.isValid) { + errors.push(labelsIssueNumValidation.error); + continue; + } // Sanitize label strings item.labels = item.labels.map( /** @param {any} label */ label => sanitizeContent(label) @@ -1411,40 +1580,43 @@ jobs: item.body = sanitizeContent(item.body); } // Validate issue_number if provided (for target "*") - if (item.issue_number !== undefined) { - if ( - typeof item.issue_number !== "number" && - typeof item.issue_number !== "string" - ) { - errors.push( - `Line ${i + 1}: update-issue 'issue_number' must be a number or string` - ); - continue; - } + const updateIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "update-issue 'issue_number'", + i + 1 + ); + if (!updateIssueNumValidation.isValid) { + errors.push(updateIssueNumValidation.error); + continue; } break; case "push-to-pr-branch": - // Validate message if provided (optional) - if (item.message !== undefined) { - if (typeof item.message !== "string") { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'message' must be a string` - ); - continue; - } - item.message = sanitizeContent(item.message); + // Validate required branch_name field + if (!item.branch_name || typeof item.branch_name !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'branch_name' string field` + ); + continue; } + // Validate required message field + if (!item.message || typeof item.message !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'message' string field` + ); + continue; + } + // Sanitize text content + item.branch_name = sanitizeContent(item.branch_name); + item.message = sanitizeContent(item.message); // Validate pull_request_number if provided (for target "*") - if (item.pull_request_number !== undefined) { - if ( - typeof item.pull_request_number !== "number" && - typeof item.pull_request_number !== "string" - ) { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'pull_request_number' must be a number or string` - ); - continue; - } + const pushPRNumValidation = validateIssueOrPRNumber( + item.pull_request_number, + "push-to-pr-branch 'pull_request_number'", + i + 1 + ); + if (!pushPRNumValidation.isValid) { + errors.push(pushPRNumValidation.error); + continue; } break; case "create-pull-request-review-comment": @@ -1456,28 +1628,17 @@ jobs: continue; } // Validate required line field - if ( - item.line === undefined || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment requires a 'line' number or string field` - ); - continue; - } - // Validate line is a positive integer - const lineNumber = - typeof item.line === "string" ? parseInt(item.line, 10) : item.line; - if ( - isNaN(lineNumber) || - lineNumber <= 0 || - !Number.isInteger(lineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'line' must be a positive integer` - ); + const lineValidation = validatePositiveInteger( + item.line, + "create-pull-request-review-comment 'line'", + i + 1 + ); + if (!lineValidation.isValid) { + errors.push(lineValidation.error); continue; } + // lineValidation.normalizedValue is guaranteed to be defined when isValid is true + const lineNumber = lineValidation.normalizedValue; // Validate required body field if (!item.body || typeof item.body !== "string") { errors.push( @@ -1488,36 +1649,24 @@ jobs: // Sanitize required text content item.body = sanitizeContent(item.body); // Validate optional start_line field - if (item.start_line !== undefined) { - if ( - typeof item.start_line !== "number" && - typeof item.start_line !== "string" - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a number or string` - ); - continue; - } - const startLineNumber = - typeof item.start_line === "string" - ? parseInt(item.start_line, 10) - : item.start_line; - if ( - isNaN(startLineNumber) || - startLineNumber <= 0 || - !Number.isInteger(startLineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a positive integer` - ); - continue; - } - if (startLineNumber > lineNumber) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` - ); - continue; - } + const startLineValidation = validateOptionalPositiveInteger( + item.start_line, + "create-pull-request-review-comment 'start_line'", + i + 1 + ); + if (!startLineValidation.isValid) { + errors.push(startLineValidation.error); + continue; + } + if ( + startLineValidation.normalizedValue !== undefined && + lineNumber !== undefined && + startLineValidation.normalizedValue > lineNumber + ) { + errors.push( + `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` + ); + continue; } // Validate optional side field if (item.side !== undefined) { @@ -1545,6 +1694,16 @@ jobs: ); continue; } + // Validate optional category field + if (item.category !== undefined) { + if (typeof item.category !== "string") { + errors.push( + `Line ${i + 1}: create-discussion 'category' must be a string` + ); + continue; + } + item.category = sanitizeContent(item.category); + } // Sanitize text content item.title = sanitizeContent(item.title); item.body = sanitizeContent(item.body); @@ -1586,22 +1745,13 @@ jobs: ); continue; } - if ( - item.line === undefined || - item.line === null || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert requires a 'line' field (number or string)` - ); - continue; - } - // Additional validation: line must be parseable as a positive integer - const parsedLine = parseInt(item.line, 10); - if (isNaN(parsedLine) || parsedLine <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${item.line})` - ); + const alertLineValidation = validatePositiveInteger( + item.line, + "create-code-scanning-alert 'line'", + i + 1 + ); + if (!alertLineValidation.isValid) { + errors.push(alertLineValidation.error); continue; } if (!item.severity || typeof item.severity !== "string") { @@ -1625,24 +1775,14 @@ jobs: continue; } // Validate optional column field - if (item.column !== undefined) { - if ( - typeof item.column !== "number" && - typeof item.column !== "string" - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a number or string` - ); - continue; - } - // Additional validation: must be parseable as a positive integer - const parsedColumn = parseInt(item.column, 10); - if (isNaN(parsedColumn) || parsedColumn <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${item.column})` - ); - continue; - } + const columnValidation = validateOptionalPositiveInteger( + item.column, + "create-code-scanning-alert 'column'", + i + 1 + ); + if (!columnValidation.isValid) { + errors.push(columnValidation.error); + continue; } // Validate optional ruleIdSuffix field if (item.ruleIdSuffix !== undefined) { diff --git a/.github/workflows/daily-test-improver.lock.yml b/.github/workflows/daily-test-improver.lock.yml index 5f28ddf34..9e136876f 100644 --- a/.github/workflows/daily-test-improver.lock.yml +++ b/.github/workflows/daily-test-improver.lock.yml @@ -2,7 +2,7 @@ # To update this file, edit the corresponding .md file and run: # gh aw compile # -# Effective stop-time: 2025-09-19 12:48:20 +# Effective stop-time: 2025-09-19 14:50:09 name: "Daily Test Coverage Improver" "on": @@ -69,7 +69,7 @@ jobs: main(); - name: Setup Safe Outputs Collector MCP env: - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true,\"update-issue\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"target\":\"*\"},\"create-issue\":{},\"create-pull-request\":{},\"update-issue\":{}}" run: | mkdir -p /tmp/safe-outputs cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF' @@ -374,8 +374,14 @@ jobs: description: "Push changes to a pull request branch", inputSchema: { type: "object", + required: ["branch_name", "message"], properties: { - message: { type: "string", description: "Optional commit message" }, + branch_name: { + type: "string", + description: + "The name of the branch to push to, should be the branch name associated with the pull request", + }, + message: { type: "string", description: "Commit message" }, pull_request_number: { type: ["number", "string"], description: "Optional pull request number for target '*'", @@ -503,7 +509,7 @@ jobs: - name: Setup MCPs env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true,\"update-issue\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"target\":\"*\"},\"create-issue\":{},\"create-pull-request\":{},\"update-issue\":{}}" run: | mkdir -p /tmp/mcp-config cat > /tmp/mcp-config/mcp-servers.json << 'EOF' @@ -541,7 +547,7 @@ jobs: WORKFLOW_NAME="Daily Test Coverage Improver" # Check stop-time limit - STOP_TIME="2025-09-19 12:48:20" + STOP_TIME="2025-09-19 14:50:09" echo "Checking stop-time limit: $STOP_TIME" # Convert stop time to epoch seconds @@ -904,7 +910,7 @@ jobs: uses: actions/github-script@v8 env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true,\"update-issue\":true}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"target\":\"*\"},\"create-issue\":{},\"create-pull-request\":{},\"update-issue\":{}}" with: script: | async function main() { @@ -1152,6 +1158,149 @@ jobs: repaired = repaired.replace(/,(\s*[}\]])/g, "$1"); return repaired; } + /** + * Validates that a value is a positive integer + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validatePositiveInteger(value, fieldName, lineNum) { + if (value === undefined || value === null) { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} is required`, + }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number or string field`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${value})`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'line' must be a positive integer`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an optional positive integer field + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validateOptionalPositiveInteger(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for specific field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a number or string`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a number or string`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a positive integer`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${value})`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an issue or pull request number (optional field) + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string}} Validation result + */ + function validateIssueOrPRNumber(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + return { isValid: true }; + } /** * Attempts to parse JSON with repair fallback * @param {string} jsonStr - The JSON string to parse @@ -1288,6 +1437,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const issueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-comment 'issue_number'", + i + 1 + ); + if (!issueNumValidation.isValid) { + errors.push(issueNumValidation.error); + continue; + } // Sanitize text content item.body = sanitizeContent(item.body); break; @@ -1336,6 +1495,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const labelsIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-labels 'issue_number'", + i + 1 + ); + if (!labelsIssueNumValidation.isValid) { + errors.push(labelsIssueNumValidation.error); + continue; + } // Sanitize label strings item.labels = item.labels.map( /** @param {any} label */ label => sanitizeContent(label) @@ -1386,40 +1555,43 @@ jobs: item.body = sanitizeContent(item.body); } // Validate issue_number if provided (for target "*") - if (item.issue_number !== undefined) { - if ( - typeof item.issue_number !== "number" && - typeof item.issue_number !== "string" - ) { - errors.push( - `Line ${i + 1}: update-issue 'issue_number' must be a number or string` - ); - continue; - } + const updateIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "update-issue 'issue_number'", + i + 1 + ); + if (!updateIssueNumValidation.isValid) { + errors.push(updateIssueNumValidation.error); + continue; } break; case "push-to-pr-branch": - // Validate message if provided (optional) - if (item.message !== undefined) { - if (typeof item.message !== "string") { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'message' must be a string` - ); - continue; - } - item.message = sanitizeContent(item.message); + // Validate required branch_name field + if (!item.branch_name || typeof item.branch_name !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'branch_name' string field` + ); + continue; } + // Validate required message field + if (!item.message || typeof item.message !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'message' string field` + ); + continue; + } + // Sanitize text content + item.branch_name = sanitizeContent(item.branch_name); + item.message = sanitizeContent(item.message); // Validate pull_request_number if provided (for target "*") - if (item.pull_request_number !== undefined) { - if ( - typeof item.pull_request_number !== "number" && - typeof item.pull_request_number !== "string" - ) { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'pull_request_number' must be a number or string` - ); - continue; - } + const pushPRNumValidation = validateIssueOrPRNumber( + item.pull_request_number, + "push-to-pr-branch 'pull_request_number'", + i + 1 + ); + if (!pushPRNumValidation.isValid) { + errors.push(pushPRNumValidation.error); + continue; } break; case "create-pull-request-review-comment": @@ -1431,28 +1603,17 @@ jobs: continue; } // Validate required line field - if ( - item.line === undefined || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment requires a 'line' number or string field` - ); - continue; - } - // Validate line is a positive integer - const lineNumber = - typeof item.line === "string" ? parseInt(item.line, 10) : item.line; - if ( - isNaN(lineNumber) || - lineNumber <= 0 || - !Number.isInteger(lineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'line' must be a positive integer` - ); + const lineValidation = validatePositiveInteger( + item.line, + "create-pull-request-review-comment 'line'", + i + 1 + ); + if (!lineValidation.isValid) { + errors.push(lineValidation.error); continue; } + // lineValidation.normalizedValue is guaranteed to be defined when isValid is true + const lineNumber = lineValidation.normalizedValue; // Validate required body field if (!item.body || typeof item.body !== "string") { errors.push( @@ -1463,36 +1624,24 @@ jobs: // Sanitize required text content item.body = sanitizeContent(item.body); // Validate optional start_line field - if (item.start_line !== undefined) { - if ( - typeof item.start_line !== "number" && - typeof item.start_line !== "string" - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a number or string` - ); - continue; - } - const startLineNumber = - typeof item.start_line === "string" - ? parseInt(item.start_line, 10) - : item.start_line; - if ( - isNaN(startLineNumber) || - startLineNumber <= 0 || - !Number.isInteger(startLineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a positive integer` - ); - continue; - } - if (startLineNumber > lineNumber) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` - ); - continue; - } + const startLineValidation = validateOptionalPositiveInteger( + item.start_line, + "create-pull-request-review-comment 'start_line'", + i + 1 + ); + if (!startLineValidation.isValid) { + errors.push(startLineValidation.error); + continue; + } + if ( + startLineValidation.normalizedValue !== undefined && + lineNumber !== undefined && + startLineValidation.normalizedValue > lineNumber + ) { + errors.push( + `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` + ); + continue; } // Validate optional side field if (item.side !== undefined) { @@ -1520,6 +1669,16 @@ jobs: ); continue; } + // Validate optional category field + if (item.category !== undefined) { + if (typeof item.category !== "string") { + errors.push( + `Line ${i + 1}: create-discussion 'category' must be a string` + ); + continue; + } + item.category = sanitizeContent(item.category); + } // Sanitize text content item.title = sanitizeContent(item.title); item.body = sanitizeContent(item.body); @@ -1561,22 +1720,13 @@ jobs: ); continue; } - if ( - item.line === undefined || - item.line === null || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert requires a 'line' field (number or string)` - ); - continue; - } - // Additional validation: line must be parseable as a positive integer - const parsedLine = parseInt(item.line, 10); - if (isNaN(parsedLine) || parsedLine <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${item.line})` - ); + const alertLineValidation = validatePositiveInteger( + item.line, + "create-code-scanning-alert 'line'", + i + 1 + ); + if (!alertLineValidation.isValid) { + errors.push(alertLineValidation.error); continue; } if (!item.severity || typeof item.severity !== "string") { @@ -1600,24 +1750,14 @@ jobs: continue; } // Validate optional column field - if (item.column !== undefined) { - if ( - typeof item.column !== "number" && - typeof item.column !== "string" - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a number or string` - ); - continue; - } - // Additional validation: must be parseable as a positive integer - const parsedColumn = parseInt(item.column, 10); - if (isNaN(parsedColumn) || parsedColumn <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${item.column})` - ); - continue; - } + const columnValidation = validateOptionalPositiveInteger( + item.column, + "create-code-scanning-alert 'column'", + i + 1 + ); + if (!columnValidation.isValid) { + errors.push(columnValidation.error); + continue; } // Validate optional ruleIdSuffix field if (item.ruleIdSuffix !== undefined) { diff --git a/.github/workflows/pr-fix.lock.yml b/.github/workflows/pr-fix.lock.yml index 3b1205367..0aeef0189 100644 --- a/.github/workflows/pr-fix.lock.yml +++ b/.github/workflows/pr-fix.lock.yml @@ -2,7 +2,7 @@ # To update this file, edit the corresponding .md file and run: # gh aw compile # -# Effective stop-time: 2025-09-19 12:48:20 +# Effective stop-time: 2025-09-19 14:50:09 name: "PR Fix" on: @@ -599,7 +599,7 @@ jobs: main(); - name: Setup Safe Outputs Collector MCP env: - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true,\"push-to-pr-branch\":{\"enabled\":true}}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{},\"create-issue\":{},\"push-to-pr-branch\":{}}" run: | mkdir -p /tmp/safe-outputs cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF' @@ -904,8 +904,14 @@ jobs: description: "Push changes to a pull request branch", inputSchema: { type: "object", + required: ["branch_name", "message"], properties: { - message: { type: "string", description: "Optional commit message" }, + branch_name: { + type: "string", + description: + "The name of the branch to push to, should be the branch name associated with the pull request", + }, + message: { type: "string", description: "Commit message" }, pull_request_number: { type: ["number", "string"], description: "Optional pull request number for target '*'", @@ -1033,7 +1039,7 @@ jobs: - name: Setup MCPs env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true,\"push-to-pr-branch\":{\"enabled\":true}}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{},\"create-issue\":{},\"push-to-pr-branch\":{}}" run: | mkdir -p /tmp/mcp-config cat > /tmp/mcp-config/mcp-servers.json << 'EOF' @@ -1071,7 +1077,7 @@ jobs: WORKFLOW_NAME="PR Fix" # Check stop-time limit - STOP_TIME="2025-09-19 12:48:20" + STOP_TIME="2025-09-19 14:50:09" echo "Checking stop-time limit: $STOP_TIME" # Convert stop time to epoch seconds @@ -1358,7 +1364,7 @@ jobs: uses: actions/github-script@v8 env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true,\"push-to-pr-branch\":{\"enabled\":true}}" + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{},\"create-issue\":{},\"push-to-pr-branch\":{}}" with: script: | async function main() { @@ -1606,6 +1612,149 @@ jobs: repaired = repaired.replace(/,(\s*[}\]])/g, "$1"); return repaired; } + /** + * Validates that a value is a positive integer + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validatePositiveInteger(value, fieldName, lineNum) { + if (value === undefined || value === null) { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} is required`, + }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for create-code-scanning-alert + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert requires a 'line' field (number or string)`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment requires a 'line' number or string field`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if (fieldName.includes("create-code-scanning-alert 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${value})`, + }; + } + if (fieldName.includes("create-pull-request-review-comment 'line'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'line' must be a positive integer`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an optional positive integer field + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string, normalizedValue?: number}} Validation result + */ + function validateOptionalPositiveInteger(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + // Match the original error format for specific field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a number or string`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a number or string`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + const parsed = typeof value === "string" ? parseInt(value, 10) : value; + if (isNaN(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + // Match the original error format for different field types + if ( + fieldName.includes("create-pull-request-review-comment 'start_line'") + ) { + return { + isValid: false, + error: `Line ${lineNum}: create-pull-request-review-comment 'start_line' must be a positive integer`, + }; + } + if (fieldName.includes("create-code-scanning-alert 'column'")) { + return { + isValid: false, + error: `Line ${lineNum}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${value})`, + }; + } + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a positive integer (got: ${value})`, + }; + } + return { isValid: true, normalizedValue: parsed }; + } + /** + * Validates an issue or pull request number (optional field) + * @param {any} value - The value to validate + * @param {string} fieldName - The name of the field being validated + * @param {number} lineNum - The line number for error reporting + * @returns {{isValid: boolean, error?: string}} Validation result + */ + function validateIssueOrPRNumber(value, fieldName, lineNum) { + if (value === undefined) { + return { isValid: true }; + } + if (typeof value !== "number" && typeof value !== "string") { + return { + isValid: false, + error: `Line ${lineNum}: ${fieldName} must be a number or string`, + }; + } + return { isValid: true }; + } /** * Attempts to parse JSON with repair fallback * @param {string} jsonStr - The JSON string to parse @@ -1742,6 +1891,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const issueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-comment 'issue_number'", + i + 1 + ); + if (!issueNumValidation.isValid) { + errors.push(issueNumValidation.error); + continue; + } // Sanitize text content item.body = sanitizeContent(item.body); break; @@ -1790,6 +1949,16 @@ jobs: ); continue; } + // Validate optional issue_number field + const labelsIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "add-labels 'issue_number'", + i + 1 + ); + if (!labelsIssueNumValidation.isValid) { + errors.push(labelsIssueNumValidation.error); + continue; + } // Sanitize label strings item.labels = item.labels.map( /** @param {any} label */ label => sanitizeContent(label) @@ -1840,40 +2009,43 @@ jobs: item.body = sanitizeContent(item.body); } // Validate issue_number if provided (for target "*") - if (item.issue_number !== undefined) { - if ( - typeof item.issue_number !== "number" && - typeof item.issue_number !== "string" - ) { - errors.push( - `Line ${i + 1}: update-issue 'issue_number' must be a number or string` - ); - continue; - } + const updateIssueNumValidation = validateIssueOrPRNumber( + item.issue_number, + "update-issue 'issue_number'", + i + 1 + ); + if (!updateIssueNumValidation.isValid) { + errors.push(updateIssueNumValidation.error); + continue; } break; case "push-to-pr-branch": - // Validate message if provided (optional) - if (item.message !== undefined) { - if (typeof item.message !== "string") { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'message' must be a string` - ); - continue; - } - item.message = sanitizeContent(item.message); + // Validate required branch_name field + if (!item.branch_name || typeof item.branch_name !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'branch_name' string field` + ); + continue; } + // Validate required message field + if (!item.message || typeof item.message !== "string") { + errors.push( + `Line ${i + 1}: push-to-pr-branch requires a 'message' string field` + ); + continue; + } + // Sanitize text content + item.branch_name = sanitizeContent(item.branch_name); + item.message = sanitizeContent(item.message); // Validate pull_request_number if provided (for target "*") - if (item.pull_request_number !== undefined) { - if ( - typeof item.pull_request_number !== "number" && - typeof item.pull_request_number !== "string" - ) { - errors.push( - `Line ${i + 1}: push-to-pr-branch 'pull_request_number' must be a number or string` - ); - continue; - } + const pushPRNumValidation = validateIssueOrPRNumber( + item.pull_request_number, + "push-to-pr-branch 'pull_request_number'", + i + 1 + ); + if (!pushPRNumValidation.isValid) { + errors.push(pushPRNumValidation.error); + continue; } break; case "create-pull-request-review-comment": @@ -1885,28 +2057,17 @@ jobs: continue; } // Validate required line field - if ( - item.line === undefined || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment requires a 'line' number or string field` - ); - continue; - } - // Validate line is a positive integer - const lineNumber = - typeof item.line === "string" ? parseInt(item.line, 10) : item.line; - if ( - isNaN(lineNumber) || - lineNumber <= 0 || - !Number.isInteger(lineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'line' must be a positive integer` - ); + const lineValidation = validatePositiveInteger( + item.line, + "create-pull-request-review-comment 'line'", + i + 1 + ); + if (!lineValidation.isValid) { + errors.push(lineValidation.error); continue; } + // lineValidation.normalizedValue is guaranteed to be defined when isValid is true + const lineNumber = lineValidation.normalizedValue; // Validate required body field if (!item.body || typeof item.body !== "string") { errors.push( @@ -1917,36 +2078,24 @@ jobs: // Sanitize required text content item.body = sanitizeContent(item.body); // Validate optional start_line field - if (item.start_line !== undefined) { - if ( - typeof item.start_line !== "number" && - typeof item.start_line !== "string" - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a number or string` - ); - continue; - } - const startLineNumber = - typeof item.start_line === "string" - ? parseInt(item.start_line, 10) - : item.start_line; - if ( - isNaN(startLineNumber) || - startLineNumber <= 0 || - !Number.isInteger(startLineNumber) - ) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be a positive integer` - ); - continue; - } - if (startLineNumber > lineNumber) { - errors.push( - `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` - ); - continue; - } + const startLineValidation = validateOptionalPositiveInteger( + item.start_line, + "create-pull-request-review-comment 'start_line'", + i + 1 + ); + if (!startLineValidation.isValid) { + errors.push(startLineValidation.error); + continue; + } + if ( + startLineValidation.normalizedValue !== undefined && + lineNumber !== undefined && + startLineValidation.normalizedValue > lineNumber + ) { + errors.push( + `Line ${i + 1}: create-pull-request-review-comment 'start_line' must be less than or equal to 'line'` + ); + continue; } // Validate optional side field if (item.side !== undefined) { @@ -1974,6 +2123,16 @@ jobs: ); continue; } + // Validate optional category field + if (item.category !== undefined) { + if (typeof item.category !== "string") { + errors.push( + `Line ${i + 1}: create-discussion 'category' must be a string` + ); + continue; + } + item.category = sanitizeContent(item.category); + } // Sanitize text content item.title = sanitizeContent(item.title); item.body = sanitizeContent(item.body); @@ -2015,22 +2174,13 @@ jobs: ); continue; } - if ( - item.line === undefined || - item.line === null || - (typeof item.line !== "number" && typeof item.line !== "string") - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert requires a 'line' field (number or string)` - ); - continue; - } - // Additional validation: line must be parseable as a positive integer - const parsedLine = parseInt(item.line, 10); - if (isNaN(parsedLine) || parsedLine <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'line' must be a valid positive integer (got: ${item.line})` - ); + const alertLineValidation = validatePositiveInteger( + item.line, + "create-code-scanning-alert 'line'", + i + 1 + ); + if (!alertLineValidation.isValid) { + errors.push(alertLineValidation.error); continue; } if (!item.severity || typeof item.severity !== "string") { @@ -2054,24 +2204,14 @@ jobs: continue; } // Validate optional column field - if (item.column !== undefined) { - if ( - typeof item.column !== "number" && - typeof item.column !== "string" - ) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a number or string` - ); - continue; - } - // Additional validation: must be parseable as a positive integer - const parsedColumn = parseInt(item.column, 10); - if (isNaN(parsedColumn) || parsedColumn <= 0) { - errors.push( - `Line ${i + 1}: create-code-scanning-alert 'column' must be a valid positive integer (got: ${item.column})` - ); - continue; - } + const columnValidation = validateOptionalPositiveInteger( + item.column, + "create-code-scanning-alert 'column'", + i + 1 + ); + if (!columnValidation.isValid) { + errors.push(columnValidation.error); + continue; } // Validate optional ruleIdSuffix field if (item.ruleIdSuffix !== undefined) {