forked from claude-did-this/claude-hub
Implement minimal-permission security model for auto-tagging operations using dedicated entrypoint scripts and CLI-based labeling to improve reliability and reduce attack surface
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
43
CLAUDE.md
43
CLAUDE.md
@@ -85,13 +85,26 @@ Use the demo repository for testing auto-tagging and webhook functionality:
|
||||
## Features
|
||||
|
||||
### Auto-Tagging
|
||||
The system automatically analyzes new issues and applies appropriate labels based on:
|
||||
The system automatically analyzes new issues and applies appropriate labels using a secure, minimal-permission approach:
|
||||
|
||||
**Security Features:**
|
||||
- **Minimal Tool Access**: Uses only `Read` and `GitHub` tools (no file editing or bash execution)
|
||||
- **Dedicated Container**: Runs in specialized container with restricted entrypoint script
|
||||
- **CLI-Based**: Uses `gh` CLI commands directly instead of JSON parsing for better reliability
|
||||
|
||||
**Label Categories:**
|
||||
- **Priority**: critical, high, medium, low
|
||||
- **Type**: bug, feature, enhancement, documentation, question, security
|
||||
- **Complexity**: trivial, simple, moderate, complex
|
||||
- **Component**: api, frontend, backend, database, auth, webhook, docker
|
||||
|
||||
When an issue is opened, Claude analyzes the title and description to suggest intelligent labels, with keyword-based fallback for reliability.
|
||||
**Process Flow:**
|
||||
1. New issue triggers `issues.opened` webhook
|
||||
2. Dedicated Claude container starts with `claudecode-tagging-entrypoint.sh`
|
||||
3. Claude analyzes issue content using minimal tools
|
||||
4. Labels applied directly via `gh issue edit --add-label` commands
|
||||
5. No comments posted (silent operation)
|
||||
6. Fallback to keyword-based labeling if CLI approach fails
|
||||
|
||||
### Automated PR Review
|
||||
The system automatically triggers comprehensive PR reviews when all checks pass:
|
||||
@@ -119,20 +132,32 @@ The system automatically triggers comprehensive PR reviews when all checks pass:
|
||||
- `awsCredentialProvider.js` - Secure AWS credential management
|
||||
- `sanitize.js` - Input sanitization and security
|
||||
|
||||
### Execution Modes
|
||||
- **Direct mode**: Runs Claude Code CLI locally
|
||||
- **Container mode**: Runs Claude in isolated Docker containers with elevated privileges
|
||||
### Execution Modes & Security Architecture
|
||||
The system uses different execution modes based on operation type:
|
||||
|
||||
### DevContainer Configuration
|
||||
The repository includes a `.devcontainer` configuration that allows Claude Code to run with:
|
||||
**Operation Types:**
|
||||
- **Auto-tagging**: Minimal permissions (`Read`, `GitHub` tools only)
|
||||
- **PR Review**: Standard permissions (full tool set)
|
||||
- **Default**: Standard permissions (full tool set)
|
||||
|
||||
**Security Features:**
|
||||
- **Tool Allowlists**: Each operation type uses specific tool restrictions
|
||||
- **Dedicated Entrypoints**: Separate container entrypoint scripts for different operations
|
||||
- **No Dangerous Permissions**: System avoids `--dangerously-skip-permissions` flag
|
||||
- **Container Isolation**: Docker containers with minimal required capabilities
|
||||
|
||||
**Container Entrypoints:**
|
||||
- `claudecode-tagging-entrypoint.sh`: Minimal tools for auto-tagging (`--allowedTools Read,GitHub`)
|
||||
- `claudecode-entrypoint.sh`: Full tools for general operations (`--allowedTools Bash,Create,Edit,Read,Write,GitHub`)
|
||||
|
||||
**DevContainer Configuration:**
|
||||
The repository includes a `.devcontainer` configuration for development:
|
||||
- Privileged mode for system-level access
|
||||
- Network capabilities (NET_ADMIN, NET_RAW) for firewall management
|
||||
- System capabilities (SYS_TIME, DAC_OVERRIDE, AUDIT_WRITE, SYS_ADMIN)
|
||||
- Docker socket mounting for container management
|
||||
- Automatic firewall initialization via post-create command
|
||||
|
||||
This configuration enables the use of `--dangerously-skip-permissions` flag when running Claude Code CLI.
|
||||
|
||||
### Workflow
|
||||
1. GitHub comment with bot mention (configured via BOT_USERNAME) triggers a webhook event
|
||||
2. Express server receives the webhook at `/api/webhooks/github`
|
||||
|
||||
79
scripts/runtime/claudecode-tagging-entrypoint.sh
Executable file
79
scripts/runtime/claudecode-tagging-entrypoint.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Minimal entrypoint for auto-tagging workflow
|
||||
# Only allows Read and GitHub tools for security
|
||||
|
||||
# Environment variables (passed from service)
|
||||
# Simply reference the variables directly - no need to reassign
|
||||
# They are already available in the environment
|
||||
|
||||
# Ensure workspace directory exists and has proper permissions
|
||||
mkdir -p /workspace
|
||||
chown -R node:node /workspace
|
||||
|
||||
# Configure GitHub authentication
|
||||
if [ -n "${GITHUB_TOKEN}" ]; then
|
||||
export GH_TOKEN="${GITHUB_TOKEN}"
|
||||
echo "${GITHUB_TOKEN}" | sudo -u node gh auth login --with-token
|
||||
sudo -u node gh auth setup-git
|
||||
else
|
||||
echo "No GitHub token provided, skipping GitHub authentication"
|
||||
fi
|
||||
|
||||
# Clone the repository as node user (needed for context)
|
||||
if [ -n "${GITHUB_TOKEN}" ] && [ -n "${REPO_FULL_NAME}" ]; then
|
||||
echo "Cloning repository ${REPO_FULL_NAME}..." >&2
|
||||
sudo -u node git clone "https://x-access-token:${GITHUB_TOKEN}@github.com/${REPO_FULL_NAME}.git" /workspace/repo >&2
|
||||
cd /workspace/repo
|
||||
else
|
||||
echo "Skipping repository clone - missing GitHub token or repository name" >&2
|
||||
cd /workspace
|
||||
fi
|
||||
|
||||
# Checkout main branch (tagging doesn't need specific branches)
|
||||
echo "Using main branch" >&2
|
||||
sudo -u node git checkout main >&2 || sudo -u node git checkout master >&2
|
||||
|
||||
# Configure git for minimal operations
|
||||
sudo -u node git config --global user.email "${BOT_EMAIL:-claude@example.com}"
|
||||
sudo -u node git config --global user.name "${BOT_USERNAME:-ClaudeBot}"
|
||||
|
||||
# Configure Anthropic API key
|
||||
export ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY}"
|
||||
|
||||
# Create response file with proper permissions
|
||||
RESPONSE_FILE="/workspace/response.txt"
|
||||
touch "${RESPONSE_FILE}"
|
||||
chown node:node "${RESPONSE_FILE}"
|
||||
|
||||
# Run Claude Code with minimal tools for auto-tagging
|
||||
echo "Running Claude Code for auto-tagging..." >&2
|
||||
|
||||
# Check if command exists
|
||||
if [ -z "${COMMAND}" ]; then
|
||||
echo "ERROR: No command provided. COMMAND environment variable is empty." | tee -a "${RESPONSE_FILE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Log the command length for debugging
|
||||
echo "Command length: ${#COMMAND}" >&2
|
||||
|
||||
# Run Claude Code with minimal tool set: Read (for repository context) and GitHub (for label operations)
|
||||
sudo -u node -E env \
|
||||
HOME="/home/node" \
|
||||
PATH="/usr/local/bin:/usr/local/share/npm-global/bin:$PATH" \
|
||||
ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY}" \
|
||||
GH_TOKEN="${GITHUB_TOKEN}" \
|
||||
/usr/local/share/npm-global/bin/claude \
|
||||
--allowedTools Read,GitHub \
|
||||
--print "${COMMAND}" \
|
||||
> "${RESPONSE_FILE}" 2>&1
|
||||
|
||||
# Check for errors
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Claude Code execution failed. See logs for details." | tee -a "${RESPONSE_FILE}" >&2
|
||||
fi
|
||||
|
||||
# Output the response
|
||||
cat "${RESPONSE_FILE}"
|
||||
@@ -127,73 +127,48 @@ async function handleWebhook(req, res) {
|
||||
);
|
||||
|
||||
try {
|
||||
// Process the issue with Claude for automatic tagging
|
||||
const tagCommand = `Analyze this issue and apply appropriate labels:
|
||||
// Process the issue with Claude for automatic tagging using CLI-based approach
|
||||
const tagCommand = `Analyze this GitHub issue and apply appropriate labels using GitHub CLI commands.
|
||||
|
||||
Title: ${issue.title}
|
||||
Description: ${issue.body || 'No description provided'}
|
||||
Issue Details:
|
||||
- Title: ${issue.title}
|
||||
- Description: ${issue.body || 'No description provided'}
|
||||
- Issue Number: ${issue.number}
|
||||
|
||||
Available labels:
|
||||
- Priority: critical, high, medium, low
|
||||
- Type: bug, feature, enhancement, documentation, question, security
|
||||
- Complexity: trivial, simple, moderate, complex
|
||||
- Component: api, frontend, backend, database, auth, webhook, docker
|
||||
Instructions:
|
||||
1. First run 'gh label list' to see what labels are available in this repository
|
||||
2. Analyze the issue content to determine appropriate labels from these categories:
|
||||
- Priority: critical, high, medium, low
|
||||
- Type: bug, feature, enhancement, documentation, question, security
|
||||
- Complexity: trivial, simple, moderate, complex
|
||||
- Component: api, frontend, backend, database, auth, webhook, docker
|
||||
3. Apply the labels using: gh issue edit ${issue.number} --add-label "label1,label2,label3"
|
||||
4. Do NOT comment on the issue - only apply labels silently
|
||||
|
||||
Return ONLY a JSON object with the labels to apply:
|
||||
{
|
||||
"labels": ["priority:medium", "type:feature", "complexity:simple", "component:api"]
|
||||
}`;
|
||||
Complete the auto-tagging task using only GitHub CLI commands.`;
|
||||
|
||||
logger.info('Sending issue to Claude for auto-tagging analysis');
|
||||
logger.info('Sending issue to Claude for CLI-based auto-tagging');
|
||||
const claudeResponse = await claudeService.processCommand({
|
||||
repoFullName: repo.full_name,
|
||||
issueNumber: issue.number,
|
||||
command: tagCommand,
|
||||
isPullRequest: false,
|
||||
branchName: null
|
||||
branchName: null,
|
||||
operationType: 'auto-tagging'
|
||||
});
|
||||
|
||||
// Parse Claude's response and apply labels
|
||||
try {
|
||||
// Extract JSON from Claude's response (it might have additional text) using safer approach
|
||||
const jsonStart = claudeResponse.indexOf('{');
|
||||
const jsonEnd = claudeResponse.lastIndexOf('}');
|
||||
if (jsonStart !== -1 && jsonEnd !== -1 && jsonEnd > jsonStart) {
|
||||
const jsonString = claudeResponse.substring(jsonStart, jsonEnd + 1);
|
||||
const labelSuggestion = JSON.parse(jsonString);
|
||||
|
||||
if (labelSuggestion.labels && Array.isArray(labelSuggestion.labels)) {
|
||||
// Apply the suggested labels
|
||||
await githubService.addLabelsToIssue({
|
||||
repoOwner: repo.owner.login,
|
||||
repoName: repo.name,
|
||||
issueNumber: issue.number,
|
||||
labels: labelSuggestion.labels
|
||||
});
|
||||
|
||||
// Auto-tagging completed - no comment needed for subtlety
|
||||
|
||||
const sanitizedLabels = sanitizeLabels(labelSuggestion.labels);
|
||||
logger.info(
|
||||
{
|
||||
repo: repo.full_name,
|
||||
issue: issue.number,
|
||||
labelCount: sanitizedLabels.length
|
||||
},
|
||||
'Auto-tagging completed successfully'
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (parseError) {
|
||||
// With CLI-based approach, Claude handles the labeling directly
|
||||
// Check if the response indicates success or if we need fallback
|
||||
if (claudeResponse.includes('error') || claudeResponse.includes('failed')) {
|
||||
logger.warn(
|
||||
{
|
||||
err: parseError,
|
||||
claudeResponseLength: claudeResponse.length,
|
||||
claudeResponsePreview: '[RESPONSE_CONTENT_REDACTED]'
|
||||
repo: repo.full_name,
|
||||
issue: issue.number,
|
||||
responsePreview: claudeResponse.substring(0, 200)
|
||||
},
|
||||
'Failed to parse Claude response for auto-tagging'
|
||||
'Claude CLI tagging may have failed, attempting fallback'
|
||||
);
|
||||
|
||||
|
||||
// Fall back to basic tagging based on keywords
|
||||
const fallbackLabels = await githubService.getFallbackLabels(issue.title, issue.body);
|
||||
if (fallbackLabels.length > 0) {
|
||||
@@ -203,7 +178,17 @@ Return ONLY a JSON object with the labels to apply:
|
||||
issueNumber: issue.number,
|
||||
labels: fallbackLabels
|
||||
});
|
||||
logger.info('Applied fallback labels successfully');
|
||||
}
|
||||
} else {
|
||||
logger.info(
|
||||
{
|
||||
repo: repo.full_name,
|
||||
issue: issue.number,
|
||||
responseLength: claudeResponse.length
|
||||
},
|
||||
'Auto-tagging completed with CLI approach'
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
|
||||
@@ -30,6 +30,7 @@ if (!BOT_USERNAME) {
|
||||
* @param {string} options.command - The command to process with Claude
|
||||
* @param {boolean} [options.isPullRequest=false] - Whether this is a pull request
|
||||
* @param {string} [options.branchName] - The branch name for pull requests
|
||||
* @param {string} [options.operationType='default'] - Operation type: 'auto-tagging', 'pr-review', or 'default'
|
||||
* @returns {Promise<string>} - Claude's response
|
||||
*/
|
||||
async function processCommand({
|
||||
@@ -37,7 +38,8 @@ async function processCommand({
|
||||
issueNumber,
|
||||
command,
|
||||
isPullRequest = false,
|
||||
branchName = null
|
||||
branchName = null,
|
||||
operationType = 'default'
|
||||
}) {
|
||||
try {
|
||||
logger.info(
|
||||
@@ -91,12 +93,59 @@ For real functionality, please configure valid GitHub and Claude API tokens.`;
|
||||
});
|
||||
}
|
||||
|
||||
// Select appropriate entrypoint script based on operation type
|
||||
let entrypointScript;
|
||||
switch (operationType) {
|
||||
case 'auto-tagging':
|
||||
entrypointScript = '/scripts/runtime/claudecode-tagging-entrypoint.sh';
|
||||
logger.info({ operationType }, 'Using minimal tools for auto-tagging operation');
|
||||
break;
|
||||
case 'pr-review':
|
||||
case 'default':
|
||||
default:
|
||||
entrypointScript = '/scripts/runtime/claudecode-entrypoint.sh';
|
||||
logger.info({ operationType }, 'Using full tool set for standard operation');
|
||||
break;
|
||||
}
|
||||
|
||||
// Create unique container name (sanitized to prevent command injection)
|
||||
const sanitizedRepoName = repoFullName.replace(/[^a-zA-Z0-9\-_]/g, '-');
|
||||
const containerName = `claude-${sanitizedRepoName}-${Date.now()}`;
|
||||
|
||||
// Create the full prompt with context and instructions
|
||||
const fullPrompt = `You are Claude, an AI assistant responding to a GitHub ${isPullRequest ? 'pull request' : 'issue'} via the ${BOT_USERNAME} webhook.
|
||||
// Create the full prompt with context and instructions based on operation type
|
||||
let fullPrompt;
|
||||
|
||||
if (operationType === 'auto-tagging') {
|
||||
fullPrompt = `You are Claude, an AI assistant analyzing a GitHub issue for automatic label assignment.
|
||||
|
||||
**Context:**
|
||||
- Repository: ${repoFullName}
|
||||
- Issue Number: #${issueNumber}
|
||||
- Operation: Auto-tagging (Read-only + Label assignment)
|
||||
|
||||
**Available Tools:**
|
||||
- Read: Access repository files and issue content
|
||||
- GitHub: Use 'gh' CLI for label operations only
|
||||
|
||||
**Task:**
|
||||
Analyze the issue and apply appropriate labels using GitHub CLI commands. Use these categories:
|
||||
- Priority: critical, high, medium, low
|
||||
- Type: bug, feature, enhancement, documentation, question, security
|
||||
- Complexity: trivial, simple, moderate, complex
|
||||
- Component: api, frontend, backend, database, auth, webhook, docker
|
||||
|
||||
**Process:**
|
||||
1. First run 'gh label list' to see available labels
|
||||
2. Analyze the issue content
|
||||
3. Use 'gh issue edit #{issueNumber} --add-label "label1,label2,label3"' to apply labels
|
||||
4. Do NOT comment on the issue - only apply labels
|
||||
|
||||
**User Request:**
|
||||
${command}
|
||||
|
||||
Complete the auto-tagging task using only the minimal required tools.`;
|
||||
} else {
|
||||
fullPrompt = `You are Claude, an AI assistant responding to a GitHub ${isPullRequest ? 'pull request' : 'issue'} via the ${BOT_USERNAME} webhook.
|
||||
|
||||
**Context:**
|
||||
- Repository: ${repoFullName}
|
||||
@@ -132,6 +181,7 @@ For real functionality, please configure valid GitHub and Claude API tokens.`;
|
||||
${command}
|
||||
|
||||
Please complete this task fully and autonomously.`;
|
||||
}
|
||||
|
||||
// Prepare environment variables for the container
|
||||
const envVars = {
|
||||
@@ -139,6 +189,7 @@ Please complete this task fully and autonomously.`;
|
||||
ISSUE_NUMBER: issueNumber || '',
|
||||
IS_PULL_REQUEST: isPullRequest ? 'true' : 'false',
|
||||
BRANCH_NAME: branchName || '',
|
||||
OPERATION_TYPE: operationType,
|
||||
COMMAND: fullPrompt,
|
||||
GITHUB_TOKEN: githubToken,
|
||||
ANTHROPIC_API_KEY: secureCredentials.get('ANTHROPIC_API_KEY')
|
||||
@@ -220,8 +271,8 @@ Please complete this task fully and autonomously.`;
|
||||
}
|
||||
});
|
||||
|
||||
// Add the image name as the final argument
|
||||
dockerArgs.push(dockerImageName);
|
||||
// Add the image name and custom entrypoint
|
||||
dockerArgs.push('--entrypoint', entrypointScript, dockerImageName);
|
||||
|
||||
// Create sanitized version for logging (remove sensitive values)
|
||||
const sanitizedArgs = dockerArgs.map(arg => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites name="jest tests" tests="34" failures="0" errors="0" time="0.301">
|
||||
<testsuite name="GitHub Controller - Check Suite Events" errors="0" failures="0" skipped="2" timestamp="2025-05-26T02:09:45" time="0.119" tests="9">
|
||||
<testcase classname="GitHub Controller - Check Suite Events should trigger PR review when check suite succeeds with PRs and combined status passes" name="GitHub Controller - Check Suite Events should trigger PR review when check suite succeeds with PRs and combined status passes" time="0.003">
|
||||
<testsuites name="jest tests" tests="34" failures="0" errors="0" time="0.703">
|
||||
<testsuite name="GitHub Controller - Check Suite Events" errors="0" failures="0" skipped="2" timestamp="2025-05-26T03:05:19" time="0.382" tests="9">
|
||||
<testcase classname="GitHub Controller - Check Suite Events should trigger PR review when check suite succeeds with PRs and combined status passes" name="GitHub Controller - Check Suite Events should trigger PR review when check suite succeeds with PRs and combined status passes" time="0.005">
|
||||
</testcase>
|
||||
<testcase classname="GitHub Controller - Check Suite Events should not trigger PR review when check suite fails" name="GitHub Controller - Check Suite Events should not trigger PR review when check suite fails" time="0">
|
||||
<testcase classname="GitHub Controller - Check Suite Events should not trigger PR review when check suite fails" name="GitHub Controller - Check Suite Events should not trigger PR review when check suite fails" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="GitHub Controller - Check Suite Events should not trigger PR review when check suite succeeds but has no PRs" name="GitHub Controller - Check Suite Events should not trigger PR review when check suite succeeds but has no PRs" time="0">
|
||||
<testcase classname="GitHub Controller - Check Suite Events should not trigger PR review when check suite succeeds but has no PRs" name="GitHub Controller - Check Suite Events should not trigger PR review when check suite succeeds but has no PRs" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="GitHub Controller - Check Suite Events should handle multiple PRs in check suite in parallel" name="GitHub Controller - Check Suite Events should handle multiple PRs in check suite in parallel" time="0.001">
|
||||
</testcase>
|
||||
@@ -22,60 +22,60 @@
|
||||
<testcase classname="GitHub Controller - Check Suite Events should skip PR review when already reviewed at same commit" name="GitHub Controller - Check Suite Events should skip PR review when already reviewed at same commit" time="0.001">
|
||||
</testcase>
|
||||
</testsuite>
|
||||
<testsuite name="GitHub Controller" errors="0" failures="0" skipped="0" timestamp="2025-05-26T02:09:45" time="0.038" tests="4">
|
||||
<testcase classname="GitHub Controller should process a valid webhook with @TestBot mention" name="GitHub Controller should process a valid webhook with @TestBot mention" time="0.001">
|
||||
<testsuite name="githubService" errors="0" failures="0" skipped="0" timestamp="2025-05-26T03:05:19" time="0.082" tests="10">
|
||||
<testcase classname="githubService getFallbackLabels should identify bug labels correctly" name="githubService getFallbackLabels should identify bug labels correctly" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="GitHub Controller should reject a webhook with invalid signature" name="GitHub Controller should reject a webhook with invalid signature" time="0.013">
|
||||
</testcase>
|
||||
<testcase classname="GitHub Controller should ignore comments without @TestBot mention" name="GitHub Controller should ignore comments without @TestBot mention" time="0">
|
||||
</testcase>
|
||||
<testcase classname="GitHub Controller should handle errors from Claude service" name="GitHub Controller should handle errors from Claude service" time="0">
|
||||
</testcase>
|
||||
</testsuite>
|
||||
<testsuite name="githubService" errors="0" failures="0" skipped="0" timestamp="2025-05-26T02:09:45" time="0.052" tests="10">
|
||||
<testcase classname="githubService getFallbackLabels should identify bug labels correctly" name="githubService getFallbackLabels should identify bug labels correctly" time="0">
|
||||
</testcase>
|
||||
<testcase classname="githubService getFallbackLabels should identify feature labels correctly" name="githubService getFallbackLabels should identify feature labels correctly" time="0.001">
|
||||
<testcase classname="githubService getFallbackLabels should identify feature labels correctly" name="githubService getFallbackLabels should identify feature labels correctly" time="0">
|
||||
</testcase>
|
||||
<testcase classname="githubService getFallbackLabels should identify enhancement labels correctly" name="githubService getFallbackLabels should identify enhancement labels correctly" time="0">
|
||||
</testcase>
|
||||
<testcase classname="githubService getFallbackLabels should identify question labels correctly" name="githubService getFallbackLabels should identify question labels correctly" time="0">
|
||||
<testcase classname="githubService getFallbackLabels should identify question labels correctly" name="githubService getFallbackLabels should identify question labels correctly" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="githubService getFallbackLabels should identify documentation labels correctly" name="githubService getFallbackLabels should identify documentation labels correctly" time="0">
|
||||
</testcase>
|
||||
<testcase classname="githubService getFallbackLabels should default to medium priority when no specific priority keywords found" name="githubService getFallbackLabels should default to medium priority when no specific priority keywords found" time="0">
|
||||
</testcase>
|
||||
<testcase classname="githubService getFallbackLabels should handle empty descriptions gracefully" name="githubService getFallbackLabels should handle empty descriptions gracefully" time="0.001">
|
||||
<testcase classname="githubService getFallbackLabels should handle empty descriptions gracefully" name="githubService getFallbackLabels should handle empty descriptions gracefully" time="0">
|
||||
</testcase>
|
||||
<testcase classname="githubService addLabelsToIssue - test mode should return mock data in test mode" name="githubService addLabelsToIssue - test mode should return mock data in test mode" time="0">
|
||||
<testcase classname="githubService addLabelsToIssue - test mode should return mock data in test mode" name="githubService addLabelsToIssue - test mode should return mock data in test mode" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="githubService createRepositoryLabels - test mode should return labels array in test mode" name="githubService createRepositoryLabels - test mode should return labels array in test mode" time="0.001">
|
||||
<testcase classname="githubService createRepositoryLabels - test mode should return labels array in test mode" name="githubService createRepositoryLabels - test mode should return labels array in test mode" time="0">
|
||||
</testcase>
|
||||
<testcase classname="githubService postComment - test mode should return mock comment data in test mode" name="githubService postComment - test mode should return mock comment data in test mode" time="0">
|
||||
<testcase classname="githubService postComment - test mode should return mock comment data in test mode" name="githubService postComment - test mode should return mock comment data in test mode" time="0.001">
|
||||
</testcase>
|
||||
</testsuite>
|
||||
<testsuite name="Claude Service" errors="0" failures="0" skipped="0" timestamp="2025-05-26T02:09:45" time="0.027" tests="4">
|
||||
<testsuite name="GitHub Controller" errors="0" failures="0" skipped="0" timestamp="2025-05-26T03:05:19" time="0.05" tests="4">
|
||||
<testcase classname="GitHub Controller should process a valid webhook with @TestBot mention" name="GitHub Controller should process a valid webhook with @TestBot mention" time="0.002">
|
||||
</testcase>
|
||||
<testcase classname="GitHub Controller should reject a webhook with invalid signature" name="GitHub Controller should reject a webhook with invalid signature" time="0.017">
|
||||
</testcase>
|
||||
<testcase classname="GitHub Controller should ignore comments without @TestBot mention" name="GitHub Controller should ignore comments without @TestBot mention" time="0">
|
||||
</testcase>
|
||||
<testcase classname="GitHub Controller should handle errors from Claude service" name="GitHub Controller should handle errors from Claude service" time="0.002">
|
||||
</testcase>
|
||||
</testsuite>
|
||||
<testsuite name="Claude Service" errors="0" failures="0" skipped="0" timestamp="2025-05-26T03:05:19" time="0.101" tests="4">
|
||||
<testcase classname="Claude Service processCommand should handle test mode correctly" name="Claude Service processCommand should handle test mode correctly" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="Claude Service processCommand should properly set up Docker command in production mode" name="Claude Service processCommand should properly set up Docker command in production mode" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="Claude Service processCommand should handle errors properly" name="Claude Service processCommand should handle errors properly" time="0.008">
|
||||
<testcase classname="Claude Service processCommand should handle errors properly" name="Claude Service processCommand should handle errors properly" time="0.011">
|
||||
</testcase>
|
||||
<testcase classname="Claude Service processCommand should handle long commands properly" name="Claude Service processCommand should handle long commands properly" time="0">
|
||||
<testcase classname="Claude Service processCommand should handle long commands properly" name="Claude Service processCommand should handle long commands properly" time="0.001">
|
||||
</testcase>
|
||||
</testsuite>
|
||||
<testsuite name="AWS Credential Provider" errors="0" failures="0" skipped="0" timestamp="2025-05-26T02:09:45" time="0.023" tests="7">
|
||||
<testcase classname="AWS Credential Provider should get credentials from AWS profile" name="AWS Credential Provider should get credentials from AWS profile" time="0">
|
||||
<testsuite name="AWS Credential Provider" errors="0" failures="0" skipped="0" timestamp="2025-05-26T03:05:20" time="0.035" tests="7">
|
||||
<testcase classname="AWS Credential Provider should get credentials from AWS profile" name="AWS Credential Provider should get credentials from AWS profile" time="0.006">
|
||||
</testcase>
|
||||
<testcase classname="AWS Credential Provider should cache credentials" name="AWS Credential Provider should cache credentials" time="0">
|
||||
<testcase classname="AWS Credential Provider should cache credentials" name="AWS Credential Provider should cache credentials" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="AWS Credential Provider should clear credential cache" name="AWS Credential Provider should clear credential cache" time="0">
|
||||
<testcase classname="AWS Credential Provider should clear credential cache" name="AWS Credential Provider should clear credential cache" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="AWS Credential Provider should get Docker environment variables" name="AWS Credential Provider should get Docker environment variables" time="0.001">
|
||||
<testcase classname="AWS Credential Provider should get Docker environment variables" name="AWS Credential Provider should get Docker environment variables" time="0">
|
||||
</testcase>
|
||||
<testcase classname="AWS Credential Provider should throw error if AWS_PROFILE is not set" name="AWS Credential Provider should throw error if AWS_PROFILE is not set" time="0.005">
|
||||
<testcase classname="AWS Credential Provider should throw error if AWS_PROFILE is not set" name="AWS Credential Provider should throw error if AWS_PROFILE is not set" time="0.006">
|
||||
</testcase>
|
||||
<testcase classname="AWS Credential Provider should throw error for non-existent profile" name="AWS Credential Provider should throw error for non-existent profile" time="0">
|
||||
<testcase classname="AWS Credential Provider should throw error for non-existent profile" name="AWS Credential Provider should throw error for non-existent profile" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="AWS Credential Provider should throw error for incomplete credentials" name="AWS Credential Provider should throw error for incomplete credentials" time="0.001">
|
||||
</testcase>
|
||||
|
||||
Reference in New Issue
Block a user