Skip to content
Testing
Skill

/n8n-security-testing

Credential exposure detection, OAuth flow validation, API key management testing, and data sanitization verification for n8n workflows. Use when validating n8n workflow security.

From plugin
agentic-qe
436200 skills169 agents149 commands
Install
$ npx -y skills add proffesor-for-testing/agentic-qe --skill n8n-security-testing --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/n8n-security-testing

Context preview

The summary Claude sees to decide when to auto-load this skill.

Credential exposure detection, OAuth flow validation, API key management testing, and data sanitization verification for n8n workflows. Use when validating n8n workflow security.

SKILL.md

n8n-security-testing.SKILL.md
name: n8n-security-testing
description: "Credential exposure detection, OAuth flow validation, API key management testing, and data sanitization verification for n8n workflows. Use when validating n8n workflow security."
category: n8n-testing
priority: critical
tokenEstimate: 1100
agents: [n8n-integration-test]
implementation_status: production
optimization_version: 1.0
last_optimized: 2025-12-15
dependencies: []
quick_reference_card: true
tags: [n8n, security, credentials, oauth, api-keys, encryption, testing]
trust_tier: 3
validation:
  schema_path: schemas/output.json
  validator_path: scripts/validate-config.json
  eval_path: evals/n8n-security-testing.yaml

n8n Security Testing

<default_to_action> When testing n8n security: 1. SCAN for credential exposure in workflows 2. VERIFY encryption of sensitive data 3. TEST OAuth token handling 4. CHECK for insecure data transmission 5. VALIDATE input sanitization

**Quick Security Checklist:**

  • No credentials in workflow JSON
  • No credentials in execution logs
  • OAuth tokens properly encrypted
  • API keys not in version control
  • Webhook authentication enabled
  • Input data sanitized

**Critical Success Factors:**

  • Scan all workflow exports
  • Test credential rotation
  • Verify encryption at rest
  • Check audit logging

</default_to_action>

Quick Reference Card

Security Risk Areas

| Area | Risk Level | Testing Focus | |------|------------|---------------| | **Credential Storage** | Critical | Encryption, exposure | | **Webhook Security** | High | Authentication, validation | | **Expression Injection** | High | Input sanitization | | **Data Leakage** | Medium | Logging, error messages | | **OAuth Flows** | Medium | Token handling, refresh |

Credential Types

| Type | Exposure Risk | Rotation | |------|---------------|----------| | **API Keys** | High if exposed | Manual | | **OAuth Tokens** | Medium (short-lived) | Automatic | | **Passwords** | Critical | Manual | | **Webhooks** | Medium | Generate new |

---

Credential Security Testing

Scan for Exposed Credentials

// Scan workflow JSON for credential exposure
async function scanForExposedCredentials(workflowId: string): Promise<CredentialScanResult> {
  const workflow = await getWorkflow(workflowId);
  const workflowJson = JSON.stringify(workflow, null, 2);

  const sensitivePatterns = [
    // API Keys
    { name: 'Generic API Key', pattern: /api[_-]?key["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi },
    { name: 'AWS Access Key', pattern: /AKIA[0-9A-Z]{16}/g },
    { name: 'AWS Secret Key', pattern: /[a-zA-Z0-9/+=]{40}/g },
    // Tokens
    { name: 'Bearer Token', pattern: /bearer\s+[a-zA-Z0-9_-]{20,}/gi },
    { name: 'JWT Token', pattern: /eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*/g },
    { name: 'Slack Token', pattern: /xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}/g },
    // Passwords
    { name: 'Password Field', pattern: /"password":\s*"[^"]+"/gi },
    { name: 'Secret Field', pattern: /"secret":\s*"[^"]+"/gi },
    // OAuth
    { name: 'Client Secret', pattern: /client[_-]?secret["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi },
    { name: 'Refresh Token', pattern: /refresh[_-]?token["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi }
  ];

  const findings: CredentialFinding[] = [];

  for (const pattern of sensitivePatterns) {
    const matches = workflowJson.match(pattern.pattern);
    if (matches) {
      for (const match of matches) {
        findings.push({
          type: pattern.name,
          location: findLocationInWorkflow(workflow, match),
          severity: 'CRITICAL',
          recommendation: `Remove ${pattern.name} from workflow. Use n8n credentials instead.`
        });
      }
    }
  }

  return {
    workflowId,
    scanned: true,
    findingsCount: findings.length,
    findings,
    secure: findings.length === 0
  };
}

Verify Credential Encryption

// Verify credentials are encrypted at rest
async function verifyCredentialEncryption(credentialId: string): Promise<EncryptionResult> {
  // Get credential metadata (not the actual credential)
  const credential = await getCredentialMetadata(credentialId);

  // Check if credential data is encrypted
  const encryptionChecks = {
    // Check if stored data looks encrypted (not plain text)
    isEncrypted: !isPlainText(credential.data),
    // Check encryption algorithm
    algorithm: credential.encryptionAlgorithm || 'unknown',
    // Check key derivation
    keyDerivation: credential.keyDerivation || 'unknown',
    // Check if using instance encryption key
    instanceEncryption: credential.useInstanceKey || false
  };

  return {
    credentialId,
    credentialName: credential.name,
    credentialType: credential.type,
    encryption: encryptionChecks,
    secure: encryptionChecks.isEncrypted && encryptionChecks.algorithm !== 'unknown',
    recommendations: generateEncryptionRecommendations(encryptionChecks)
  };
}

// Check if data appears to be plain text
function isPlainText(data: string): boolean {
  // Plain text credentials often have recognizable patterns
  const plainTextPatterns = [
    /^[a-zA-Z0-9_-]+$/, // Simple alphanumeric
    /^sk-[a-zA-Z0-9]+$/, // API key format
    /^Bearer\s/, // Bearer token
  ];

  return plainTextPatterns.some(p => p.test(data));
}

Test Credential Rotation

// Test credential rotation process
async function testCredentialRotation(credentialId: string): Promise<RotationTestResult> {
  const credential = await getCredentialMetadata(credentialId);

  const rotationTests = {
    // Check if credential has rotation metadata
    hasRotationSchedule: !!credential.rotationSchedule,
    lastRotated: credential.lastRotatedAt,
    rotationDue: isRotationDue(credential),

    // Test OAuth token refresh
    oauthRefresh: credential.type.includes('oauth')
      ? await testOAuthRefresh(credentialId)
      : null,

    // Check credential age
    credentialAge: calculateAge(credential.createdAt
Read more
Ships withagentic-qe

AI-powered quality engineering agents that generate tests, find coverage gaps, detect flaky tests, and learn your codebase patterns — across 11 coding agent platforms.

Get the whole plugin

Other skills on agentic-qe.