/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.
$ npx -y skills add proffesor-for-testing/agentic-qe --skill n8n-security-testing --agent claude-codeHow 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.mdname: 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.createdAtRead more
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.createdAtAI-powered quality engineering agents that generate tests, find coverage gaps, detect flaky tests, and learn your codebase patterns — across 11 coding agent platforms.
Repo: proffesor-for-testing/agentic-qe
Other skills on agentic-qe.
- /a11y-ally
Use when running comprehensive WCAG accessibility audits with axe-core + pa11y + Lighthouse, generating context-aware remediation, or testing video accessibility. Supports 3-tier browser cascade with graceful degradation.
Open skill - /accessibility-testing
WCAG 2.2 compliance testing, screen reader validation, and inclusive design verification. Use when ensuring legal compliance (ADA, Section 508), testing for disabilities, or building accessible applications for 1 billion disabled users globally.
Open skill - /agentdb-advanced
Master advanced AgentDB features including QUIC synchronization, multi-database management, custom distance metrics, hybrid search, and distributed systems integration. Use when building distributed AI systems, multi-agent coordination, or advanced vector search applications.
Open skill - /agentdb-learning
Create and train AI learning plugins with AgentDB's 9 reinforcement learning algorithms. Includes Decision Transformer, Q-Learning, SARSA, Actor-Critic, and more. Use when building self-learning agents, implementing RL, or optimizing agent behavior through experience.
Open skill - /agentdb-memory-patterns
Implement persistent memory patterns for AI agents using AgentDB. Includes session memory, long-term storage, pattern learning, and context management. Use when building stateful agents, chat systems, or intelligent assistants.
Open skill - /agentdb-optimization
Optimize AgentDB performance with quantization (4-32x memory reduction), HNSW indexing (150x faster search), caching, and batch operations. Use when optimizing memory usage, improving search speed, or scaling to millions of vectors.
Open skill

