{
  "name": "OSS Package Health - daily exact-version security scan",
  "nodes": [
    {
      "parameters": {
        "content": "# OSS Package Health\n\n1. Attach an n8n Header Auth credential to **Run OSS exact-version scan**. Use header `Authorization` and value `Bearer YOUR_APIFY_API_TOKEN`.\n2. Edit that node's JSON body with the exact versions you actually monitor. Do not use ranges or `latest`.\n3. Test once, then replace **Connect Slack or email here** with your private notification node.\n\nThe workflow keeps vulnerabilities, CISA KEV matches, source failures, and version drift. Every run uses the Actor's published pay-per-event pricing.",
        "height": 340,
        "width": 500,
        "color": 5
      },
      "id": "f2c34c87-0c4a-4d16-bbd2-5fdaf7232d0c",
      "name": "Setup notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -640,
        -300
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * *"
            }
          ]
        }
      },
      "id": "3b1fe3d7-095d-4c05-aa56-04ae629eff67",
      "name": "Every morning",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -580,
        80
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.apify.com/v2/actors/ai-coding-radar~oss-package-health-monitor/run-sync-get-dataset-items?clean=1",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "{\n  \"packages\": [\n    {\"name\": \"lodash\", \"ecosystem\": \"npm\", \"version\": \"4.17.20\"},\n    {\"name\": \"requests\", \"ecosystem\": \"PyPI\", \"version\": \"2.31.0\"}\n  ],\n  \"includeDownloads\": true,\n  \"includeVulnerabilities\": true,\n  \"includeRepository\": true,\n  \"includeCisaKev\": true\n}",
        "options": {
          "timeout": 120000
        }
      },
      "id": "0e2fb650-9600-4aa5-851e-4a29bfaa53ab",
      "name": "Run OSS exact-version scan",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -300,
        80
      ]
    },
    {
      "parameters": {
        "jsCode": "const entries = $input.all();\nconst rows = entries.flatMap(({ json }) => {\n  if (Array.isArray(json)) return json;\n  if (json && Array.isArray(json.body)) return json.body;\n  if (json && Array.isArray(json.data)) return json.data;\n  if (json && Array.isArray(json.items)) return json.items;\n  return json && typeof json === 'object' ? [json] : [];\n});\nconst hasItems = value => Array.isArray(value) && value.length > 0;\nconst actionable = rows.filter(row => row && (\n  hasItems(row.vulnerabilities) ||\n  hasItems(row.cisaKevMatches) ||\n  row.status === 'error' ||\n  row.status === 'partial' ||\n  row.isLatest === false\n));\nreturn actionable.map(row => {\n  const type = hasItems(row.cisaKevMatches) ? 'cisa_kev'\n    : hasItems(row.vulnerabilities) ? 'vulnerability'\n    : row.status === 'error' || row.status === 'partial' ? 'source_failure'\n    : 'version_drift';\n  const detail = type === 'cisa_kev' ? `${row.cisaKevMatches.length} CISA KEV match(es)`\n    : type === 'vulnerability' ? `${row.vulnerabilities.length} OSV vulnerability record(s)`\n    : type === 'source_failure' ? `${row.status}: ${(row.errors || []).join('; ')}`\n    : `checked ${row.checkedVersion || 'unknown'}, latest ${row.latestVersion || 'unknown'}`;\n  return { json: { ...row, alertType: type, alertText: `${row.package} ${row.ecosystem} ${row.requestedVersion || row.checkedVersion || ''}: ${detail}`.trim() } };\n});"
      },
      "id": "cb4da5a9-c6d9-411e-89be-e036557e42a6",
      "name": "Keep security findings",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -20,
        80
      ]
    },
    {
      "parameters": {},
      "id": "7a6bef1a-c6a3-47bb-bf82-b9955fef4ac7",
      "name": "Connect Slack or email here",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        280,
        80
      ]
    }
  ],
  "connections": {
    "Every morning": {
      "main": [
        [
          {
            "node": "Run OSS exact-version scan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run OSS exact-version scan": {
      "main": [
        [
          {
            "node": "Keep security findings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Keep security findings": {
      "main": [
        [
          {
            "node": "Connect Slack or email here",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "Asia/Shanghai",
    "saveManualExecutions": true
  },
  "versionId": "be6e35de-52d1-4447-bf89-0ed0c7ac70b4",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}