๐ต๏ธโโ๏ธ ์๋ GitHub ์ค์บ๋ - ๋ ธ์ถ๋ AWS IAM ํค ๊ฐ์ง
์ด๊ฒ์SecOps๋ถ์ผ์์๋ํ ์ํฌํ๋ก์ฐ๋ก, 18๊ฐ์ ๋ ธ๋๋ฅผ ํฌํจํฉ๋๋ค.์ฃผ๋ก If, Code, Wait, Slack, HttpRequest ๋ฑ์ ๋ ธ๋๋ฅผ ์ฌ์ฉํ๋ฉฐ. ์๋ GitHub ์ค์บ๋ - ๋ ธ์ถ๋ AWS IAM ํค ๊ฐ์ง
- โขSlack Bot Token ๋๋ Webhook URL
- โข๋์ API์ ์ธ์ฆ ์ ๋ณด๊ฐ ํ์ํ ์ ์์
์ฌ์ฉ๋ ๋ ธ๋ (18)
์นดํ ๊ณ ๋ฆฌ
{
"id": "Tcy2xebw2oGWMxUN",
"meta": {
"instanceId": "c62c01f3e843893075a10f252ec7d6d69e5ab593af019f50055d506cb3081b99",
"templateCredsSetupCompleted": true
},
"name": "๐ต๏ธโโ๏ธ Automated GitHub Scanner for Exposed AWS IAM Keys",
"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 Scanner for Exposed AWS IAM Keys โ Quick Overview\n\n## โ๏ธ Workflow at a Glance\n\n1. ๐ **Automated Discovery** \n Scans GitHub repositories for exposed AWS IAM access keys associated with your AWS account.\n\n2. ๐ง **Intelligent Filtering** \n Processes only active access keys and eliminates duplicate findings.\n\n3. ๐จ **Risk Assessment** \n Evaluates exposure severity and assigns risk levels (High/Medium/Low) based on findings.\n\n4. ๐ฃ **Actionable Alerts** \n Sends detailed Slack notifications with interactive buttons for immediate response.\n\n5. ๐ ๏ธ **Remediation Guidance** \n Provides step-by-step instructions for securing compromised credentials.\n\n6. ๐ **Continuous Monitoring** \n Maintains ongoing surveillance to detect new exposures quickly.\n\n---\n\n*๐ก๏ธ This workflow helps security teams quickly identify and respond to potential security breaches from exposed AWS credentials.*\n"
},
"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 ์ฌ์ฉ์ ๋ชฉ๋ก1",
"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": "## ๐จ Automate Disable Access Keys\n\n### 1. Prerequisites\n\n- Configure AWS credentials in n8n with appropriate IAM permissions \n- Create an `Extract_Key_Info` node that provides the compromised key's ID and username \n\n### 2. Workflow Integration\n\n- Add this node after your key detection logic \n- Connect it to trigger when a compromised key is confirmed \n\n### 3. Security Considerations\n\n- Ensure your AWS credentials have the `iam:UpdateAccessKey` permission \n- Consider adding verification steps before automatic disabling \n- Implement notification steps after key disabling (e.g., Slack, email) \n\n---\n\nThis implementation enables **immediate remediation of security risks** by automatically disabling compromised AWS access keys as soon as they're discovered in GitHub repositories.\n"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "d96b586a-4739-4ef5-84c8-c71ff9f3e1e6",
"connections": {
"158340f1-4dc4-4b4c-933f-95a28be524dc": {
"main": [
[
{
"node": "f2634090-b035-4592-ad56-b970c2bff14c",
"type": "main",
"index": 0
}
]
]
},
"714724b6-9aaf-4d17-97b7-1a3fd3dcaeae": {
"main": [
[
{
"node": "a1448a68-81aa-4571-8f62-617c57daa6e6",
"type": "main",
"index": 0
}
]
]
},
"ef29b02b-f2a4-4642-a5c4-c9685b0e2c22": {
"main": [
[
{
"node": "8f9db031-245e-4c00-bdb9-4ed55e9b57b2",
"type": "main",
"index": 0
}
]
]
},
"f2634090-b035-4592-ad56-b970c2bff14c": {
"main": [
[
{
"node": "b2c1157f-c1eb-4d22-95d0-6ec166a93a4a",
"type": "main",
"index": 0
}
]
]
},
"c0300832-418b-4fa8-98b6-2b799f2bcfe6": {
"main": [
[
{
"node": "158340f1-4dc4-4b4c-933f-95a28be524dc",
"type": "main",
"index": 0
}
]
]
},
"ae5a0e4f-892c-4a75-bdcc-36253fbc7274": {
"main": [
[
{
"node": "0946f05d-11f0-480b-934a-c996bbb9551e",
"type": "main",
"index": 0
}
]
]
},
"a1448a68-81aa-4571-8f62-617c57daa6e6": {
"main": [
[
{
"node": "b2c1157f-c1eb-4d22-95d0-6ec166a93a4a",
"type": "main",
"index": 0
}
]
]
},
"3ed685c7-27b5-4a00-840a-6f9dddff6d3f": {
"main": [
[
{
"node": "ef29b02b-f2a4-4642-a5c4-c9685b0e2c22",
"type": "main",
"index": 0
}
]
]
},
"0946f05d-11f0-480b-934a-c996bbb9551e": {
"main": [
[
{
"node": "3ed685c7-27b5-4a00-840a-6f9dddff6d3f",
"type": "main",
"index": 0
}
]
]
},
"a6cd482c-86d2-49ac-aefe-6beec92c98af": {
"main": [
[
{
"node": "75ed15ec-fdee-4644-a83c-6b47005dcac1",
"type": "main",
"index": 0
}
]
]
},
"57753be8-33d2-4172-94a2-0d5a4f7c73ec": {
"main": [
[
{
"node": "c0300832-418b-4fa8-98b6-2b799f2bcfe6",
"type": "main",
"index": 0
}
]
]
},
"75ed15ec-fdee-4644-a83c-6b47005dcac1": {
"main": [
[
{
"node": "57753be8-33d2-4172-94a2-0d5a4f7c73ec",
"type": "main",
"index": 0
}
]
]
},
"b2c1157f-c1eb-4d22-95d0-6ec166a93a4a": {
"main": [
[],
[
{
"node": "ae5a0e4f-892c-4a75-bdcc-36253fbc7274",
"type": "main",
"index": 0
}
]
]
},
"8f9db031-245e-4c00-bdb9-4ed55e9b57b2": {
"main": [
[
{
"node": "a6cd482c-86d2-49ac-aefe-6beec92c98af",
"type": "main",
"index": 0
}
]
]
},
"84edf6c4-fc87-4b34-86c6-ad72c15cafe8": {
"main": [
[
{
"node": "714724b6-9aaf-4d17-97b7-1a3fd3dcaeae",
"type": "main",
"index": 0
}
]
]
}
}
}์ด ์ํฌํ๋ก์ฐ๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์?
์์ JSON ๊ตฌ์ฑ ์ฝ๋๋ฅผ ๋ณต์ฌํ์ฌ n8n ์ธ์คํด์ค์์ ์ ์ํฌํ๋ก์ฐ๋ฅผ ์์ฑํ๊ณ "JSON์์ ๊ฐ์ ธ์ค๊ธฐ"๋ฅผ ์ ํํ ํ, ๊ตฌ์ฑ์ ๋ถ์ฌ๋ฃ๊ณ ํ์์ ๋ฐ๋ผ ์ธ์ฆ ์ค์ ์ ์์ ํ์ธ์.
์ด ์ํฌํ๋ก์ฐ๋ ์ด๋ค ์๋๋ฆฌ์ค์ ์ ํฉํ๊ฐ์?
๊ณ ๊ธ - ๋ณด์ ์ด์
์ ๋ฃ์ธ๊ฐ์?
์ด ์ํฌํ๋ก์ฐ๋ ์์ ํ ๋ฌด๋ฃ์ด๋ฉฐ ์ง์ ๊ฐ์ ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ค๋ง, ์ํฌํ๋ก์ฐ์์ ์ฌ์ฉํ๋ ํ์ฌ ์๋น์ค(์: OpenAI API)๋ ์ฌ์ฉ์ ์ง์ ๋น์ฉ์ ์ง๋ถํด์ผ ํ ์ ์์ต๋๋ค.
๊ด๋ จ ์ํฌํ๋ก์ฐ ์ถ์ฒ
Niranjan G
@niranjanCybersecurity leader turning complex workflows into seamless, AI-driven automations.
์ด ์ํฌํ๋ก์ฐ ๊ณต์