8
n8n 中文网amn8n.com

🕵️‍♂️ 自动GitHub扫描器 - 检测暴露的AWS IAM密钥

高级

这是一个SecOps领域的自动化工作流,包含 18 个节点。主要使用 If, Code, Wait, Slack, HttpRequest 等节点。 自动GitHub扫描器 - 检测暴露的AWS IAM密钥

前置要求
  • Slack Bot Token 或 Webhook URL
  • 可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "Tcy2xebw2oGWMxUN",
  "meta": {
    "instanceId": "c62c01f3e843893075a10f252ec7d6d69e5ab593af019f50055d506cb3081b99",
    "templateCredsSetupCompleted": true
  },
  "name": "🕵️‍♂️ 自动 GitHub 扫描器 - 检测暴露的 AWS IAM 密钥",
  "tags": [
    {
      "id": "XuoVybTXeUXuim6G",
      "name": "✅ Live",
      "createdAt": "2025-06-08T07:59:43.586Z",
      "updatedAt": "2025-06-08T07:59:43.586Z"
    },
    {
      "id": "MbPHhZHgb39Syuoa",
      "name": "🔐 SecOps",
      "createdAt": "2025-04-20T05:18:20.689Z",
      "updatedAt": "2025-06-08T08:01:56.494Z"
    }
  ],
  "nodes": [
    {
      "id": "84edf6c4-fc87-4b34-86c6-ad72c15cafe8",
      "name": "当点击\"执行工作流\"时",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -880,
        75
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "b2c1157f-c1eb-4d22-95d0-6ec166a93a4a",
      "name": "拆分用户进行处理",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -220,
        75
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "ae5a0e4f-892c-4a75-bdcc-36253fbc7274",
      "name": "获取用户访问密钥",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        0
      ],
      "parameters": {
        "url": "https://iam.amazonaws.com",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "Action",
              "value": "ListAccessKeys"
            },
            {
              "name": "Version",
              "value": "2010-05-08"
            },
            {
              "name": "UserName",
              "value": "={{ $json.username }}"
            }
          ]
        },
        "nodeCredentialType": "aws"
      },
      "credentials": {
        "aws": {
          "id": "Y0EXCbx12345678",
          "name": "AWS account"
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "0946f05d-11f0-480b-934a-c996bbb9551e",
      "name": "仅筛选活动密钥",
      "type": "n8n-nodes-base.if",
      "position": [
        220,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "active-key-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.ListAccessKeysResponse.ListAccessKeysResult.AccessKeyMetadata[0].Status }}",
              "rightValue": "Active"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "8f9db031-245e-4c00-bdb9-4ed55e9b57b2",
      "name": "在 GitHub 中搜索暴露的密钥",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        880,
        0
      ],
      "parameters": {
        "url": "={{ $json.simpleSearch.searchUrl }}",
        "options": {
          "timeout": 10000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi"
      },
      "credentials": {
        "githubApi": {
          "id": "3fz93A0mgyxzXGOL",
          "name": "GitHub account"
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "a6cd482c-86d2-49ac-aefe-6beec92c98af",
      "name": "聚合搜索结果",
      "type": "n8n-nodes-base.code",
      "position": [
        1100,
        0
      ],
      "parameters": {
        "jsCode": "// Aggregate all search results\nconst allResults = [];\nconst accessKeyId = $input.first().json.accessKeyId;\nconst userName = $input.first().json.userName;\n\nfor (const item of $input.all()) {\n  if (item.json.total_count > 0) {\n    allResults.push(...item.json.items);\n  }\n}\n\n// Remove duplicates based on repository and file path\nconst uniqueResults = allResults.filter((item, index, self) => \n  index === self.findIndex(t => t.repository.full_name === item.repository.full_name && t.path === item.path)\n);\n\nreturn [{\n  accessKeyId,\n  userName,\n  totalMatches: uniqueResults.length,\n  repositories: uniqueResults,\n  isCompromised: uniqueResults.length > 0\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "75ed15ec-fdee-4644-a83c-6b47005dcac1",
      "name": "检查被泄露的密钥",
      "type": "n8n-nodes-base.if",
      "position": [
        1320,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ebf33a67-f8f3-4700-8579-1d33b7a642d5",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "isCompromised",
              "rightValue": "={{ $json.isCompromised }}"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "57753be8-33d2-4172-94a2-0d5a4f7c73ec",
      "name": "生成安全报告",
      "type": "n8n-nodes-base.code",
      "position": [
        1540,
        0
      ],
      "parameters": {
        "jsCode": "// Generate comprehensive security report - Notification Only (No Automatic Actions)\nconst data = $input.item.json;\n\nconst report = {\n  timestamp: new Date().toISOString(),\n  accessKeyId: data.accessKeyId,\n  userName: data.userName,\n  status: data.isCompromised ? 'COMPROMISED' : 'SAFE',\n  totalRepositories: data.totalMatches,\n  repositories: data.repositories.map(repo => ({\n    name: repo.repository.full_name,\n    url: repo.html_url,\n    path: repo.path,\n    score: repo.score\n  })),\n  actionTaken: data.isCompromised ? 'Security team notified - Manual review required' : 'No action required',\n  riskLevel: data.totalMatches > 5 ? 'HIGH' : data.totalMatches > 0 ? 'MEDIUM' : 'LOW',\n  notificationSent: data.isCompromised ? true : false,\n  requiresManualAction: data.isCompromised ? true : false,\n  recommendedActions: data.isCompromised ? [\n    'Manually disable the compromised access key',\n    'Generate new access keys for affected services',\n    'Remove exposed keys from repositories',\n    'Audit recent API usage for this key'\n  ] : []\n};\n\nreturn [report];"
      },
      "typeVersion": 2
    },
    {
      "id": "f2634090-b035-4592-ad56-b970c2bff14c",
      "name": "继续扫描",
      "type": "n8n-nodes-base.noOp",
      "position": [
        2340,
        80
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "158340f1-4dc4-4b4c-933f-95a28be524dc",
      "name": "Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        2020,
        0
      ],
      "webhookId": "47dc1fb0-71c0-40f6-8263-88880f01a436",
      "parameters": {
        "text": "={{ $json.message }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "C08Q2H8SRUP"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "id": "i1yhHILyYn12345678",
          "name": "Slack account"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "c0300832-418b-4fa8-98b6-2b799f2bcfe6",
      "name": "格式化 Slack 警报",
      "type": "n8n-nodes-base.code",
      "position": [
        1760,
        0
      ],
      "parameters": {
        "jsCode": "/**\n * GitHub Scanner for Exposed AWS IAM Keys - Data Processor\n * \n * This script processes the results from the AWS IAM Key scanner workflow\n * and formats them for reporting and notification purposes.\n */\n\n// Main entry point - this is what n8n will call\n// Input: items array from n8n workflow\n// Output: Slack Block Kit UI format (blocks array only)\nconst items = $input.all();\n\n// Process the input data\nconst scanResults = items.map(item => item.json);\nconst processedResults = processIAMScanResults(scanResults);\n\n// Format for markdown notification\nconst markdownNotification = formatForMarkdownNotification(processedResults);\n// Return as array of objects for n8n\nreturn [{ message: markdownNotification }];\n\n/**\n * Process scan results and format them for reporting\n * @param {Array} scanResults - Array of scan result objects from the workflow\n * @returns {Array} Array of formatted result objects\n */\nfunction processIAMScanResults(scanResults) {\n  if (!Array.isArray(scanResults) || scanResults.length === 0) {\n    return [{\n      status: 'NO_RESULTS',\n      timestamp: new Date().toISOString(),\n      message: 'No scan results to process'\n    }];\n  }\n\n  // Process each scan result\n  const processedResults = scanResults.map(result => {\n    // Create a deep copy to avoid modifying the original data\n    const processedResult = JSON.parse(JSON.stringify(result));\n    \n    // Ensure we have the required fields\n    if (!processedResult.timestamp) {\n      processedResult.timestamp = new Date().toISOString();\n    }\n    \n    // Calculate risk metrics if not provided\n    if (!processedResult.riskLevel) {\n      processedResult.riskLevel = calculateRiskLevel(processedResult);\n    }\n    \n    // Generate recommended actions if not provided\n    if (!processedResult.recommendedActions || processedResult.recommendedActions.length === 0) {\n      processedResult.recommendedActions = generateRecommendedActions(processedResult);\n    }\n    \n    return processedResult;\n  });\n\n  return processedResults;\n}\n  \n  /**\n   * Calculate risk level based on scan result data\n   * @param {Object} result - Individual scan result\n   * @returns {string} Risk level (HIGH, MEDIUM, LOW)\n   */\n  function calculateRiskLevel(result) {\n    // Default to MEDIUM if we can't determine\n    if (!result || !result.status) {\n      return 'MEDIUM';\n    }\n    \n    // Determine risk level based on status and other factors\n    if (result.status === 'COMPROMISED') {\n      // Public repositories or repositories with many contributors increase risk\n      if (result.repositories && result.repositories.some(repo => {\n        return repo.score >= 0.8; // High score indicates higher risk\n      })) {\n        return 'HIGH';\n      }\n      return 'MEDIUM';\n    } else if (result.status === 'POTENTIAL_EXPOSURE') {\n      return 'MEDIUM';\n    } else {\n      return 'LOW';\n    }\n  }\n  \n  /**\n   * Generate recommended actions based on scan result\n   * @param {Object} result - Individual scan result\n   * @returns {Array} List of recommended actions\n   */\n  function generateRecommendedActions(result) {\n    const commonActions = [\n      'Review the exposure details in the security report',\n      'Rotate affected access keys immediately'\n    ];\n    \n    if (result.status === 'COMPROMISED') {\n      return [\n        ...commonActions,\n        'Disable the compromised access key',\n        'Generate new access keys for affected services',\n        'Remove exposed keys from repositories',\n        'Audit recent API usage for this key'\n      ];\n    } else if (result.status === 'POTENTIAL_EXPOSURE') {\n      return [\n        ...commonActions,\n        'Verify if the exposure is a false positive',\n        'Consider rotating keys as a precaution'\n      ];\n    } else {\n      return [\n        'No immediate action required',\n        'Continue monitoring for potential exposures'\n      ];\n    }\n  }\n  \n  /**\n   * Format scan results for Slack-compatible markdown notification\n   * @param {Array} processedResults - Processed scan results\n   * @returns {string} Slack-compatible markdown formatted notification text\n   */\n  function formatForMarkdownNotification(processedResults) {\n    if (!Array.isArray(processedResults) || processedResults.length === 0) {\n      return `🔒 *AWS IAM Key Scan Completed*\\n\\nNo exposed AWS IAM keys were detected in this scan.`;\n    }\n  \n    // Count compromised keys\n    const compromisedCount = processedResults.filter(r => r.status === 'COMPROMISED').length;\n    \n    if (compromisedCount === 0) {\n      return `🔒 *AWS IAM Key Scan Completed*\\n\\nNo exposed AWS IAM keys were detected in this scan.`;\n    }\n    \n    // Create Slack-compatible notification for compromised keys\n    let markdown = `⚠️ *ALERT: AWS IAM Keys Exposed*\\n\\n*${compromisedCount}* AWS IAM keys have been potentially exposed on GitHub. Immediate action required.\\n\\n`;\n    \n    // Add details for each compromised key\n    processedResults.filter(r => r.status === 'COMPROMISED').forEach((result, index) => {\n      markdown += `*Exposure ${index + 1}*\\n\\n`;\n      \n      // Add repository information in a cleaner format\n      markdown += `*Exposure Details:*\\n`;\n      result.repositories.forEach(repo => {\n        markdown += `• *Repository:* ${repo.name}\\n`;\n        markdown += `• *Path:* \\`${repo.path}\\`\\n`;\n        markdown += `• *Risk Score:* ${repo.score.toFixed(2)}\\n\\n`;\n      });\n      \n      // Add risk level and action taken in a compact format\n      markdown += `*Risk Level:* ${result.riskLevel} | *Action Taken:* ${result.actionTaken || 'None'}\\n\\n`;\n      \n      // Add recommended actions with better formatting\n      markdown += `*Recommended Actions:*\\n`;\n      result.recommendedActions.forEach(action => {\n        markdown += `• ${action}\\n`;\n      });\n      markdown += `\\n`;\n    });\n    \n    return markdown;\n  }\n\n  \n  // Export functions for use in n8n workflow\n  module.exports = {\n    processIAMScanResults,\n    calculateRiskLevel,\n    generateRecommendedActions,\n    formatForMarkdownNotification\n  };"
      },
      "typeVersion": 2
    },
    {
      "id": "3ed685c7-27b5-4a00-840a-6f9dddff6d3f",
      "name": "准备 GitHub 搜索",
      "type": "n8n-nodes-base.code",
      "position": [
        440,
        0
      ],
      "parameters": {
        "jsCode": "/**\n * Simplified GitHub Search for AWS Access Keys\n * This script generates optimized search queries for finding exposed AWS access keys on GitHub\n */\n\n/**\n * Generates a GitHub API search URL for a given AWS access key\n * @param {Object} inputData - The input data containing AWS access key information\n * @returns {Object} - Search information including URL and query\n */\nfunction generateGitHubSearchQuery(inputData) {\n  try {\n    // Extract access key information from input\n    let accessKeyId;\n    let userName;\n    \n    // Handle different input formats\n    if (inputData && typeof inputData === 'object') {\n      // Check for n8n workflow format\n      if (inputData.ListAccessKeysResponse && \n          inputData.ListAccessKeysResponse.ListAccessKeysResult && \n          inputData.ListAccessKeysResponse.ListAccessKeysResult.AccessKeyMetadata && \n          Array.isArray(inputData.ListAccessKeysResponse.ListAccessKeysResult.AccessKeyMetadata) && \n          inputData.ListAccessKeysResponse.ListAccessKeysResult.AccessKeyMetadata.length > 0) {\n        \n        accessKeyId = inputData.ListAccessKeysResponse.ListAccessKeysResult.AccessKeyMetadata[0].AccessKeyId;\n        userName = inputData.ListAccessKeysResponse.ListAccessKeysResult.AccessKeyMetadata[0].UserName;\n      } \n      // Direct object format\n      else if (inputData.accessKeyId) {\n        accessKeyId = inputData.accessKeyId;\n        userName = inputData.userName || 'unknown';\n      }\n    }\n    \n    // Validate we have an access key\n    if (!accessKeyId) {\n      console.error('No valid AWS access key ID found in input');\n      return { error: 'No valid AWS access key ID found in input' };\n    }\n    \n    // Create the most effective search query\n    // Based on testing, the exact match with quotes is most reliable\n    const searchQuery = `\"${accessKeyId}\" in:file`;\n    \n    // Generate the GitHub API search URL\n    const searchUrl = `https://api.github.com/search/code?q=${encodeURIComponent(searchQuery)}`;\n    \n    return {\n      accessKeyId,\n      userName,\n      searchQuery,\n      searchUrl,\n      note: 'A single exact match query is typically sufficient for finding exposed AWS keys'\n    };\n  } catch (error) {\n    console.error('Error generating GitHub search query:', error);\n    return { error: `Error generating GitHub search query: ${error.message}` };\n  }\n}\n\n/**\n * For more comprehensive searches, this function generates multiple search patterns\n * This is optional and can be used if the simple search doesn't yield results\n * @param {string} accessKeyId - The AWS access key ID to search for\n * @returns {Array} - Array of search queries and URLs\n */\nfunction generateComprehensiveSearch(accessKeyId) {\n  if (!accessKeyId) {\n    return { error: 'No access key ID provided' };\n  }\n  \n  // Search patterns for access keys (in order of effectiveness)\n  const searchQueries = [\n    `\"${accessKeyId}\" in:file`,           // Exact match (most effective)\n    `${accessKeyId} in:file`,              // Without quotes (catches more results but may have false positives)\n    `AWS_ACCESS_KEY_ID=${accessKeyId} in:file`,  // Environment variable format\n    `aws_access_key_id: ${accessKeyId} in:file`, // YAML/config format\n    `accessKeyId: ${accessKeyId} in:file`        // JavaScript/JSON format\n  ];\n  \n  return searchQueries.map(query => ({\n    searchQuery: query,\n    searchUrl: `https://api.github.com/search/code?q=${encodeURIComponent(query)}`\n  }));\n}\n\n// For n8n integration\nfunction processForN8n() {\n  try {\n    // Get input data\n    const inputData = $input.item.json;\n    \n    // Generate the simplified search (recommended approach)\n    const simpleSearch = generateGitHubSearchQuery(inputData);\n    \n    // For backward compatibility, also generate the comprehensive search\n    // if an access key was successfully extracted\n    let comprehensiveSearch = [];\n    if (simpleSearch.accessKeyId) {\n      comprehensiveSearch = generateComprehensiveSearch(simpleSearch.accessKeyId);\n    }\n    \n    return {\n      simpleSearch,\n      comprehensiveSearch,\n      recommendation: 'The simple search is recommended for most cases. Only use comprehensive search if simple search yields no results.'\n    };\n  } catch (error) {\n    console.error('Error in n8n processing:', error);\n    return { error: `Error in n8n processing: ${error.message}` };\n  }\n}\n\n// For standalone usage\nif (typeof module !== 'undefined' && module.exports) {\n  module.exports = {\n    generateGitHubSearchQuery,\n    generateComprehensiveSearch\n  };\n} else {\n  // For n8n execution\n  return processForN8n();\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "e58f0cd8-13df-41eb-9a9c-99efc7eba0c4",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -900,
        -680
      ],
      "parameters": {
        "width": 1120,
        "height": 580,
        "content": "# 🔐🕵️‍♂️ GitHub 扫描器 - 检测暴露的 AWS IAM 密钥 - 快速概览"
      },
      "typeVersion": 1
    },
    {
      "id": "ef29b02b-f2a4-4642-a5c4-c9685b0e2c22",
      "name": "速率限制等待",
      "type": "n8n-nodes-base.wait",
      "position": [
        660,
        0
      ],
      "webhookId": "850e337e-d261-456a-86c1-1725d77f9e52",
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "714724b6-9aaf-4d17-97b7-1a3fd3dcaeae",
      "name": "列出 AWS 用户",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -660,
        80
      ],
      "parameters": {
        "url": "https://iam.amazonaws.com",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "Action",
              "value": "ListUsers"
            },
            {
              "name": "Version",
              "value": "2010-05-08"
            }
          ]
        },
        "nodeCredentialType": "aws"
      },
      "credentials": {
        "aws": {
          "id": "Y0EXCbx12345678",
          "name": "AWS account"
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "a1448a68-81aa-4571-8f62-617c57daa6e6",
      "name": "提取 AWS 用户名",
      "type": "n8n-nodes-base.code",
      "position": [
        -440,
        75
      ],
      "parameters": {
        "jsCode": "// n8n Function node: Extract usernames from AWS ListUsersResponse\n\nconst allInputs = $input.all();\n\n// Find the ListUsersResponse input\nconst listUsersInput = allInputs.find(item => item.json.ListUsersResponse);\n\nif (!listUsersInput) {\n  throw new Error('No ListUsersResponse found in input data');\n}\n\n// Extract users array from the response\nconst users = listUsersInput.json\n  .ListUsersResponse\n  .ListUsersResult\n  .Users || [];\n\n// Extract usernames and return each as a separate item for looping\nconst usernameItems = users.map(user => ({\n  json: { username: user.UserName }\n}));\n\n// Return array of individual username objects\nreturn usernameItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "2339c570-93a3-4c1a-ae0d-2eb9810eeee8",
      "name": "禁用访问密钥",
      "type": "n8n-nodes-base.httpRequest",
      "disabled": true,
      "position": [
        1200,
        -620
      ],
      "parameters": {
        "url": "https://iam.amazonaws.com",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "Action",
              "value": "UpdateAccessKey"
            },
            {
              "name": "AccessKeyId",
              "value": "AKIAEXAMPLEACCESSKEY1"
            },
            {
              "name": "UserName",
              "value": "user@domain.com"
            },
            {
              "name": "Status",
              "value": "Inactive"
            },
            {
              "name": "Version",
              "value": "2010-05-08"
            }
          ]
        },
        "nodeCredentialType": "aws"
      },
      "credentials": {
        "aws": {
          "id": "Y0EXCbx12345678",
          "name": "AWS account"
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "99938eb1-580b-4a91-b7a7-83464b33053e",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        -680
      ],
      "parameters": {
        "color": 6,
        "width": 1200,
        "height": 580,
        "content": "## 🚨 自动禁用访问密钥"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "d96b586a-4739-4ef5-84c8-c71ff9f3e1e6",
  "connections": {
    "Slack": {
      "main": [
        [
          {
            "node": "Continue Scanning",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List AWS Users1": {
      "main": [
        [
          {
            "node": "Extract AWS Usernames",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Wait": {
      "main": [
        [
          {
            "node": "Search GitHub for Exposed Keys",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Continue Scanning": {
      "main": [
        [
          {
            "node": "Split Users for Processing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Slack Alert": {
      "main": [
        [
          {
            "node": "Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get User Access Keys": {
      "main": [
        [
          {
            "node": "Filter Active Keys Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract AWS Usernames": {
      "main": [
        [
          {
            "node": "Split Users for Processing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Github Search": {
      "main": [
        [
          {
            "node": "Rate Limit Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Active Keys Only": {
      "main": [
        [
          {
            "node": "Prepare Github Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Search Results": {
      "main": [
        [
          {
            "node": "Check For Compromised Keys",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Security Report": {
      "main": [
        [
          {
            "node": "Format Slack Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check For Compromised Keys": {
      "main": [
        [
          {
            "node": "Generate Security Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Users for Processing": {
      "main": [
        [],
        [
          {
            "node": "Get User Access Keys",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search GitHub for Exposed Keys": {
      "main": [
        [
          {
            "node": "Aggregate Search Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking ‘Execute workflow’": {
      "main": [
        [
          {
            "node": "List AWS Users1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 安全运维

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量18
分类1
节点类型9
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者
Niranjan G

Niranjan G

@niranjan

Cybersecurity leader turning complex workflows into seamless, AI-driven automations.

外部链接
在 n8n.io 查看

分享此工作流