{
  "name": "SIGMA to PPL Converter",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "sigma-to-ppl",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-trigger",
      "name": "Webhook - Receive SIGMA",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        250,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Extract SIGMA rule from webhook body\nconst body = $input.item.json.body || $input.item.json;\nconst sigmaRule = body.sigma_rule || body.rule || body;\n\n// Validate input\nif (!sigmaRule) {\n  throw new Error('No SIGMA rule provided in request body. Expected: {\"sigma_rule\": \"...\"}');\n}\n\n// Construct the prompt for Ollama\nconst systemPrompt = `You are an expert in SIGMA detection rules and OpenSearch/Amazon Security Lake Piped Processing Language (PPL). Your task is to convert SIGMA rules to PPL queries.`;\n\nconst conversionInstructions = `\n# SIGMA to PPL Conversion Guidelines\n\n## SIGMA Logsource Mapping\n- Windows process_creation \u2192 source=windows_logs OR source=security_lake.ocsf.process_activity\n- Network connection \u2192 source=network_logs OR source=security_lake.ocsf.network_activity  \n- File events \u2192 source=file_logs OR source=security_lake.ocsf.file_activity\n- Registry events \u2192 source=registry_logs\n\n## SIGMA Field Mapping to PPL\n- Image \u2192 process_name or process.name\n- CommandLine \u2192 command_line or process.command_line\n- ParentImage \u2192 parent_process_name\n- User \u2192 user_name or actor.user.name\n- DestinationIp \u2192 destination_ip or dst_endpoint.ip\n- DestinationPort \u2192 destination_port or dst_endpoint.port\n- FileName \u2192 file_name or file.name\n- FileHash \u2192 file_hash or file.hashes\n\n## SIGMA Modifiers to PPL\n- contains \u2192 like '%value%'\n- startswith \u2192 like 'value%'\n- endswith \u2192 like '%value'\n- all \u2192 AND conditions\n- any/1 of \u2192 OR conditions  \n- exact match \u2192 = 'value'\n- in (list) \u2192 in ('value1', 'value2')\n\n## PPL Query Structure\nsource=<log_source>\n| where <condition1>\n| where <condition2>\n| fields <output_fields>\n| stats count() by <group_fields>\n\n## Example Conversions\n\n### Example 1: PowerShell Execution\nSIGMA:\n```yaml\ndetection:\n  selection:\n    Image|endswith: '\\\\powershell.exe'\n    CommandLine|contains: '-encodedcommand'\n  condition: selection\n```\n\nPPL:\n```ppl\nsource=windows_logs\n| where process_name like '%powershell.exe'\n| where command_line like '%encodedcommand%'\n```\n\n### Example 2: Outbound Network Connection\nSIGMA:\n```yaml\ndetection:\n  selection:\n    DestinationIp:\n      - '192.168.1.1'\n      - '10.0.0.1'\n    DestinationPort: 443\n  condition: selection\n```\n\nPPL:\n```ppl\nsource=network_logs\n| where destination_ip in ('192.168.1.1', '10.0.0.1')\n| where destination_port = 443\n```\n\n### Example 3: File Creation with Hash\nSIGMA:\n```yaml\ndetection:\n  selection:\n    EventType: 'FileCreate'\n    FileName|contains: '\\\\temp\\\\'\n    FileHash: 'a1b2c3d4e5f6'\n  condition: selection  \n```\n\nPPL:\n```ppl\nsource=file_logs\n| where event_type = 'FileCreate'\n| where file_name like '%\\\\temp\\\\%'\n| where file_hash = 'a1b2c3d4e5f6'\n```\n\n# Your Task\nConvert the following SIGMA rule to PPL. Only output the PPL query, no explanations.\n`;\n\nconst userPrompt = `${conversionInstructions}\\n\\nSIGMA Rule to Convert:\\n\\`\\`\\`yaml\\n${sigmaRule}\\n\\`\\`\\`\\n\\nPPL Query:`;\n\n// Prepare Ollama API request\nreturn {\n  json: {\n    model: $env.OLLAMA_MODEL || 'llama3.1:8b',\n    prompt: userPrompt,\n    system: systemPrompt,\n    stream: false,\n    options: {\n      temperature: 0.1,\n      top_p: 0.9,\n      num_predict: 500\n    }\n  }\n};"
      },
      "id": "prepare-prompt",
      "name": "Prepare Ollama Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        450,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.OLLAMA_HOST || \"http://host.docker.internal:11434\" }}/api/generate",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json) }}",
        "options": {
          "timeout": 60000
        }
      },
      "id": "call-ollama",
      "name": "Call Ollama LLM",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        650,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse Ollama response and extract PPL query\nconst response = $input.item.json.response || '';\n\n// Clean up the response\nlet pplQuery = response.trim();\n\n// Remove markdown code blocks if present\npplQuery = pplQuery.replace(/```ppl\\n?/gi, '');\npplQuery = pplQuery.replace(/```\\n?/g, '');\n\n// Remove leading/trailing whitespace\npplQuery = pplQuery.trim();\n\n// Validate that we got something that looks like PPL\nif (!pplQuery.toLowerCase().includes('source=')) {\n  throw new Error('Generated output does not appear to be valid PPL. Response: ' + response);\n}\n\nreturn {\n  json: {\n    ppl_query: pplQuery,\n    model_used: $node[\"Prepare Ollama Prompt\"].json.model,\n    original_sigma: $node[\"Webhook - Receive SIGMA\"].json.body.sigma_rule,\n    timestamp: new Date().toISOString(),\n    success: true\n  }\n};"
      },
      "id": "parse-response",
      "name": "Parse PPL Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        850,
        300
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json, null, 2) }}",
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        }
      },
      "id": "respond-webhook",
      "name": "Respond with PPL",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1050,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Error handler - format error response\nconst error = $input.item.json.error || 'Unknown error occurred';\n\nreturn {\n  json: {\n    success: false,\n    error: error.toString(),\n    timestamp: new Date().toISOString(),\n    hint: 'Check that Ollama is running and accessible at ' + ($env.OLLAMA_HOST || 'http://host.docker.internal:11434')\n  }\n};"
      },
      "id": "error-handler",
      "name": "Error Handler",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        850,
        500
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json, null, 2) }}",
        "options": {
          "responseCode": 500,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        }
      },
      "id": "respond-error",
      "name": "Respond with Error",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1050,
        500
      ]
    }
  ],
  "connections": {
    "Webhook - Receive SIGMA": {
      "main": [
        [
          {
            "node": "Prepare Ollama Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Ollama Prompt": {
      "main": [
        [
          {
            "node": "Call Ollama LLM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Call Ollama LLM": {
      "main": [
        [
          {
            "node": "Parse PPL Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse PPL Response": {
      "main": [
        [
          {
            "node": "Respond with PPL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Handler": {
      "main": [
        [
          {
            "node": "Respond with Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": ""
  },
  "versionId": "1",
  "tags": [
    {
      "name": "sigma",
      "createdAt": "2024-10-27T00:00:00.000Z",
      "updatedAt": "2024-10-27T00:00:00.000Z"
    },
    {
      "name": "ppl",
      "createdAt": "2024-10-27T00:00:00.000Z",
      "updatedAt": "2024-10-27T00:00:00.000Z"
    },
    {
      "name": "ollama",
      "createdAt": "2024-10-27T00:00:00.000Z",
      "updatedAt": "2024-10-27T00:00:00.000Z"
    }
  ]
}