{
  "id": "GAODuENBveqRdHlq",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Monitor GitHub Repositories for Unauthorized Actions",
  "tags": [],
  "nodes": [
    {
      "id": "fec58465-8d55-497b-8550-b2571ef4cebf",
      "name": "Schedule",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        688,
        608
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3e4b9b99-152f-4a49-a358-fb3756068da7",
      "name": "Filter Member Events",
      "type": "n8n-nodes-base.code",
      "position": [
        1136,
        608
      ],
      "parameters": {
        "jsCode": "// This node filters the raw GitHub events for security risks\nconst items = $input.all();\n\nreturn items.filter(item => {\n  // Define what you consider a security event\n  // you may leave out PushEvents e.g. commits, other less critical non-sec changes\n  const criticalEvents = [\n    'MemberEvent', // Someone added/removed from repo\n    'PublicEvent', // Repo made public (VERY IMPORTANT)\n    'TeamAddEvent', // Team access changed\n    'PushEvent' // To See every commit event\n  ];\n  return criticalEvents.includes(item.json.type);\n});"
      },
      "typeVersion": 1
    },
    {
      "id": "4d1f6e99-53c5-4662-b26d-229d018b52b0",
      "name": "Check Whitelist",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        1344,
        608
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "github_username",
              "keyValue": "={{ $json.type === 'MemberEvent' ? $json.payload.member.login : $json.actor.login }}"
            }
          ]
        },
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "7mEomjLDrAwIBwFl",
          "cachedResultUrl": "/projects/A5ypxGvZ8T1gIWVL/datatables/7mEomjLDrAwIBwFl",
          "cachedResultName": "it_whitelist"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "27e8f26a-e630-4f77-8bbf-4a8eb1f23e90",
      "name": "Github Events (HTTP)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        928,
        608
      ],
      "parameters": {
        "url": "https://api.github.com/repos/<user_name>/<repository>/events",
        "options": {},
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/vnd.github+json"
            },
            {
              "name": "X-GitHub-Api-Version",
              "value": "2022-11-28"
            }
          ]
        },
        "nodeCredentialType": "githubApi"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "48130fa9-634d-4755-b16e-503394ac44ab",
      "name": "Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        1552,
        592
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "3f93352d-3e9b-413f-87e1-a792602d5418",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Filter Member Events').item.json.type }}",
                    "rightValue": "MemberEvent"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "e607b280-bce6-4d14-b65d-f13b522b141a",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Filter Member Events').item.json.type }}",
                    "rightValue": "PublicEvent"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "184f10a0-60cc-43af-9bc0-85e273b6ffcd",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Filter Member Events').item.json.type }}",
                    "rightValue": "PushEvent"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "9d8319f3-948e-4453-94b4-1d0a7067a456",
      "name": "PublicEvent",
      "type": "n8n-nodes-base.if",
      "position": [
        1760,
        608
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "d560f6d6-cdbe-4abf-b9bb-bf2096d07d2f",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.role }}",
              "rightValue": "admin"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "ed17c63b-dec3-41df-a533-c7e57e902f51",
      "name": "PushEvent",
      "type": "n8n-nodes-base.if",
      "position": [
        1760,
        768
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "08a9cbfc-6db1-4c47-b21f-a195f4551b94",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json.role }}",
              "rightValue": "="
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "31f6a4b0-4a65-46a5-ae7c-6db969fdffc8",
      "name": "MemberEvent",
      "type": "n8n-nodes-base.if",
      "position": [
        1760,
        448
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "40be187c-e40f-4781-a911-0bc6c4cc1357",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $('Filter Member Events').item.json.type }}",
              "rightValue": "admin"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "e90ae829-d324-4c26-a3ea-f755b87ca245",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        208
      ],
      "parameters": {
        "width": 608,
        "height": 784,
        "content": "\n## Monitor GitHub Repositories for Unauthorized Actions\n\n### How it works:\n\nThis workflow monitors GitHub for high-risk activities to ensure that only authorized users can modify the repository. It periodically polls GitHub for events such as PushEvent, MemberEvent, and PublicEvent.\n\nFor each event, the workflow extracts the username of actor and looks it up in the it_whitelist data table to determine the user\u2019s role. A Switch node then routes the event to the appropriate validation logic.\n\n\u2022 Member & Public events: Only users with the admin role are allowed. Any non-admin action such as adding a repository member or changing a private repository to public triggers an alert.\n\u2022 Push events: The user must exist in the whitelist. If no role is found, the user is treated as unknown and flagged.\n\nAll unauthorized actions are reported to Slack, including the event type, actor name, and repository details.\n\n### Setup Steps:\nCredentials: Connect your GitHub Personal Access Token and Slack Bot Token.\n\nCreate a Data Table named it_whitelist with columns github_username and role. Add your GitHub username with the role admin to prevent self-alerts. Accordingly, add other developers and members in your organization with appropriate roles to white list them.\n\nSwitch Configuration: Use the expression {{ $item.json.type }} in 'Rules' mode to route events.\n\nLogic: \nConfigure the PushEvent IF node to flag users whose role is empty or missing.\nConfigure the Member and Public IF nodes to flag users whose role is not admin."
      },
      "typeVersion": 1
    },
    {
      "id": "b6f4481b-bcc3-45c0-bef6-cca5a5c43ca3",
      "name": "Push Event Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        2160,
        752
      ],
      "parameters": {
        "text": "=\ud83d\udea8 Security Alert: Unauthorized {{ $('Filter Member Events').item.json.type }} by {{ $json.actor.login }}!",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0917N0QN2C",
          "cachedResultName": "content-creation-agent"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "396f4a16-2432-4911-9ea4-27d04c94892f",
      "name": "Public Event Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        2160,
        592
      ],
      "parameters": {
        "text": "epository {{ $json.repository.name }} was made PUBLIC by {{ $json.actor.login }}! \ud83d\udea8 Immediate review required.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0917N0QN2C",
          "cachedResultName": "content-creation-agent"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "fe332c78-fee3-4802-bcee-0fd3395696e6",
      "name": "Member Event Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        2160,
        432
      ],
      "parameters": {
        "text": "Unauthorized MemberEvent! Actor: {{ $json.actor.login }} tried to add {{ $json.payload.member.login }}. \u26a0\ufe0f This action was blocked/flagged because the actor is not an Admin.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0917N0QN2C",
          "cachedResultName": "content-creation-agent"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "489f1636-61db-4375-8f6c-a0c1eaf84bf5",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        368
      ],
      "parameters": {
        "color": 7,
        "width": 576,
        "height": 384,
        "content": "## GitHub Events & Identity Check\n\nGitHub Node: Polls the API every 10 minutes to fetch the latest events and activity logs.\n\nFilter: Passes only Member, Public, and Push events while ignoring irrelevant data.\n\nCheck \"Whitelist\" Datatable : Searches the it_whitelist Data Table for the authorized users and user's role (admin/developer)."
      },
      "typeVersion": 1
    },
    {
      "id": "dc7b7e02-a468-4d72-b27b-7eb1ae2b57db",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        208
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 768,
        "content": "## Switch and If Nodes to apply Security rules\n\nSwitch: Evaluates the event type (such as PushEvent) and routes the workflow into three dedicated branches, allowing event-specific security policies to be applied.\n\nFlag the activity as unauthorized using If:\n\u2013 the event is Member or Public and the user\u2019s role is not admin, or\n\u2013 the event is a Push and the user is not found in the whitelist (role is empty or unavailable).\n\nOverall, rules are set such that data is sent to True output to send Alerts only when a security violation is detected."
      },
      "typeVersion": 1
    },
    {
      "id": "e3cac6f2-eeac-4274-9a71-944bee482de5",
      "name": "No Operation, do nothing",
      "type": "n8n-nodes-base.noOp",
      "position": [
        1984,
        832
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "27e410da-5e06-4ff3-abff-1bdbd289a574",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2128,
        208
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 768,
        "content": "## Slack Node (Alert System)\nNotify: Posts a message containing the event type and the GitHub username of the person who triggered it.\n\nTrigger: Only fires when a violation flows through the True path of a security check node."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "7e87b531-024e-4d39-bed9-91e42b52ec47",
  "connections": {
    "Switch": {
      "main": [
        [
          {
            "node": "MemberEvent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "PublicEvent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "PushEvent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule": {
      "main": [
        [
          {
            "node": "Github Events (HTTP)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PushEvent": {
      "main": [
        [
          {
            "node": "Push Event Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "MemberEvent": {
      "main": [
        [
          {
            "node": "Member Event Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PublicEvent": {
      "main": [
        [
          {
            "node": "Public Event Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Whitelist": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Member Events": {
      "main": [
        [
          {
            "node": "Check Whitelist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Github Events (HTTP)": {
      "main": [
        [
          {
            "node": "Filter Member Events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}