{
  "version": "Notebook/1.0",
  "items": [
    {
      "type": 1,
      "content": {
        "json": "# Google Threat Intelligence\n\nSubmit lookups/scans to Google Threat Intelligence (GTI) playbooks directly from Microsoft Sentinel and review the results in place, without leaving the portal. Select a tab below to get started:\n\n- **Vulnerability Enrichment** — enter a CVE ID to trigger the `GTIVulnerabilityEnrichment` playbook and view risk rating, CVSS v3/v4 scores, EPSS score, CISA KEV due date, exploitation state, and executive summary.\n- **Private URL Scanning** — submit a URL to the `GTIURLScanEnrichment` playbook and view the GTI verdict, severity, threat score, and Safe Browsing verdict. *(Requires a GTI Private Scanning license.)*\n- **Private File Scanning** — submit a file already stored in Azure Blob Storage to the `GTIFileScanEnrichment` playbook (or have it scanned automatically via `GTIFileScanBlobEnrichment`) and view the threat verdict, severity, type, and size. *(Requires a GTI Private Scanning license.)*\n\nEach tab's corresponding playbook must be deployed (from this Solution) and left enabled before that tab will work. On each tab: pick the **Subscription ID** and **Resource Group** where that playbook is deployed, fill in the required parameters, click the action button and confirm, then click the refresh icon on the status tile once the playbook finishes to load the result."
      },
      "name": "text - overview"
    },
    {
      "type": 11,
      "content": {
        "version": "LinkItem/1.0",
        "style": "tabs",
        "links": [
          {
            "id": "ae6e889d-5cd3-4a5e-9393-eb2408c22c5c",
            "cellValue": "dashboard",
            "linkTarget": "parameter",
            "linkLabel": "Vulnerability Enrichment",
            "subTarget": "vuln_enrichment",
            "style": "link"
          },
          {
            "id": "83236a50-6f99-4029-830a-dcc0e12fc144",
            "cellValue": "dashboard",
            "linkTarget": "parameter",
            "linkLabel": "Private URL Scanning",
            "subTarget": "url_scan",
            "style": "link"
          },
          {
            "id": "61bf6905-ada0-461c-84a3-2b4165b71e68",
            "cellValue": "dashboard",
            "linkTarget": "parameter",
            "linkLabel": "Private File Scanning",
            "subTarget": "file_scan",
            "style": "link"
          }
        ]
      },
      "name": "links - tabs"
    },
    {
      "type": 12,
      "content": {
        "version": "NotebookGroup/1.0",
        "groupType": "editable",
        "items": [
          {
            "type": 1,
            "content": {
              "json": "## Vulnerability Enrichment"
            },
            "name": "text - vuln - title"
          },
          {
            "type": 1,
            "content": {
              "json": "This page depends on the **GTIVulnerabilityEnrichment** playbook which is deployed with the Microsoft Sentinel Solution.</br>\nPlease deploy this playbook first and keep it enabled in order to use this page.",
              "style": "info"
            },
            "name": "text - vuln - info"
          },
          {
            "type": 1,
            "content": {
              "json": "## Vulnerability Enrichment Workbook\n\n**Purpose**: Enter a CVE ID and trigger the **GTIVulnerabilityEnrichment** playbook to retrieve the latest vulnerability intelligence (risk rating, CVSS, EPSS, CISA KEV status, exploitation state, and executive summary) from Google Threat Intelligence, right from within Microsoft Sentinel.\n\n### Setup (2 steps)\n1. **Select Environment**: Choose the **Subscription ID** and **Resource Group** where the GTIVulnerabilityEnrichment playbook is deployed.\n2. **Enter CVE ID**: Type the CVE identifier to look up, e.g. `CVE-2024-1234`.\n\n### Run Enrichment\n1. Click **\"Get Vulnerability Intelligence\"**.\n2. In the confirmation panel, review the details and click **\"Get Vulnerability Intelligence\"** to confirm.\n3. The GTIVulnerabilityEnrichment Logic App fetches the CVE's intelligence from GTI and ingests it into the `GTI_Vulnerabilities_CL` table (surfaced via the `GTIVulnerabilities` parser).\n\n### View Results\n1. Wait a few moments for ingestion to complete, then click the refresh icon on the status tile below.\n2. Once the tile displays **\"Click here to populate data.\"**, click it (or click Refresh on the panel) to load the **Latest Vulnerability Intelligence** table for that CVE.\n3. The **Vulnerability Enrichment Results** grid further below always shows the full enrichment history for all CVE IDs within the selected Time Range.\n\n### Important Notes\n- **Progress Tracking**: Monitor the GTIVulnerabilityEnrichment Logic App run history in the Azure Portal if data doesn't appear after a few minutes.\n- **No Data Yet**: If the status tile shows \"Click on refresh icon to check data availability\", the playbook run has not ingested data yet — click refresh again after a short wait.",
              "style": "upsell"
            },
            "name": "text - vuln - howto"
          },
          {
            "type": 9,
            "content": {
              "version": "KqlParameterItem/1.0",
              "parameters": [
                {
                  "id": "3194e69f-2e4f-4024-969d-8b634d652a7f",
                  "version": "KqlParameterItem/1.0",
                  "name": "TimeRange",
                  "label": "Time Range",
                  "type": 4,
                  "description": "Time window applied to the Vulnerability Enrichment Results grid and the status/latest-result lookups below. Does not affect the playbook run itself.",
                  "isRequired": true,
                  "typeSettings": {
                    "selectableValues": [
                      {
                        "durationMs": 300000
                      },
                      {
                        "durationMs": 900000
                      },
                      {
                        "durationMs": 1800000
                      },
                      {
                        "durationMs": 3600000
                      },
                      {
                        "durationMs": 14400000
                      },
                      {
                        "durationMs": 43200000
                      },
                      {
                        "durationMs": 86400000
                      },
                      {
                        "durationMs": 172800000
                      },
                      {
                        "durationMs": 259200000
                      },
                      {
                        "durationMs": 604800000
                      },
                      {
                        "durationMs": 2592000000
                      },
                      {
                        "durationMs": 7776000000
                      }
                    ]
                  },
                  "value": {
                    "durationMs": 3600000
                  }
                },
                {
                  "id": "b6e45351-713a-4936-bd16-2f5d5fcd9aaa",
                  "version": "KqlParameterItem/1.0",
                  "name": "SubscriptionId",
                  "label": "Subscription ID",
                  "type": 2,
                  "description": "Select the Azure Subscription where the GTIVulnerabilityEnrichment playbook (Logic App) is deployed.",
                  "isRequired": true,
                  "query": "Resources\n| distinct subscriptionId",
                  "typeSettings": {
                    "showDefault": false
                  },
                  "timeContext": {
                    "durationMs": 86400000
                  },
                  "queryType": 1,
                  "resourceType": "microsoft.resourcegraph/resources",
                  "value": null
                },
                {
                  "id": "6a14a771-53bb-4fbe-a973-79c7180c0bb8",
                  "version": "KqlParameterItem/1.0",
                  "name": "ResourceGroup",
                  "label": "Resource Group",
                  "type": 2,
                  "description": "Select the Resource Group where the GTIVulnerabilityEnrichment playbook (Logic App) is deployed.",
                  "isRequired": true,
                  "query": "Resources\n| extend ResourceGroupName = resourceGroup\n| distinct ResourceGroupName",
                  "typeSettings": {
                    "showDefault": false
                  },
                  "timeContext": {
                    "durationMs": 86400000
                  },
                  "queryType": 1,
                  "resourceType": "microsoft.resourcegraph/resources",
                  "value": null
                },
                {
                  "id": "7cab47aa-b53c-4ac9-ac9f-a31bc535b4f0",
                  "version": "KqlParameterItem/1.0",
                  "name": "CveId",
                  "label": "CVE ID",
                  "type": 1,
                  "description": "Enter a CVE ID, e.g. CVE-2024-1234",
                  "isRequired": true,
                  "timeContext": {
                    "durationMs": 86400000
                  }
                }
              ],
              "style": "pills",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces"
            },
            "name": "parameters - vuln"
          },
          {
            "type": 11,
            "content": {
              "version": "LinkItem/1.0",
              "style": "paragraph",
              "links": [
                {
                  "id": "26fb684c-ec21-4999-b19a-198604f2744b",
                  "cellValue": "",
                  "linkTarget": "ArmAction",
                  "linkLabel": "Get Vulnerability Intelligence",
                  "style": "primary",
                  "linkIsContextBlade": true,
                  "armActionContext": {
                    "path": "/subscriptions/{SubscriptionId}/resourceGroups/{ResourceGroup}/providers/Microsoft.Logic/workflows/GTIVulnerabilityEnrichment/triggers/manual/run?api-version=2016-10-01",
                    "body": "{\n  \"cveId\": \"{CveId}\"\n}",
                    "httpMethod": "POST",
                    "description": "This will trigger the GTIVulnerabilityEnrichment playbook to fetch the latest intelligence for the supplied CVE ID from Google Threat Intelligence.",
                    "actionName": "Playbook Trigger",
                    "runLabel": "Get Vulnerability Intelligence"
                  }
                }
              ]
            },
            "conditionalVisibilities": [
              {
                "parameterName": "SubscriptionId",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "ResourceGroup",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "CveId",
                "comparison": "isNotEqualTo"
              }
            ],
            "name": "links - vuln - submit"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "let dummy_table = datatable(TimeGenerated: datetime, CveId: string) [];\nunion isfuzzy=true dummy_table,\n(GTIVulnerabilities\n| where CveId =~ '{CveId}')\n| summarize count()\n| extend status = case(count_ == 0, \"Click on refresh icon to check data availability\", \"Click here to populate data.\")\n| project status",
              "size": 3,
              "timeContextFromParameter": "TimeRange",
              "showRefreshButton": true,
              "exportFieldName": "status",
              "exportParameterName": "status",
              "exportDefaultValue": "",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "visualization": "tiles",
              "tileSettings": {
                "titleContent": {
                  "columnMatch": "status"
                },
                "showBorder": false,
                "size": "full"
              }
            },
            "customWidth": "30",
            "conditionalVisibilities": [
              {
                "parameterName": "SubscriptionId",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "ResourceGroup",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "CveId",
                "comparison": "isNotEqualTo"
              }
            ],
            "name": "query - vuln - status",
            "styleSettings": {
              "margin": "28px"
            }
          },
          {
            "type": 12,
            "content": {
              "version": "NotebookGroup/1.0",
              "groupType": "editable",
              "items": [
                {
                  "type": 3,
                  "content": {
                    "version": "KqlItem/1.0",
                    "query": "let dummy_table = datatable(TimeGenerated: datetime, CveId: string) [];\nlet mainTable = union isfuzzy=true dummy_table, GTIVulnerabilities\n| where CveId =~ '{CveId}'\n| top 1 by TimeGenerated desc;\nmainTable\n| summarize arg_max(TimeGenerated, *) by CveId\n| extend\n    ['CVE ID'] = CveId,\n    ['Name'] = VulnName,\n    ['Priority'] = Priority,\n    ['Risk Rating'] = RiskRating,\n    ['Predicted Risk Rating'] = PredictedRiskRating,\n    ['Exploitation State'] = ExploitationState,\n    ['CVSS v3.x Score'] = Cvssv3xBaseScore,\n    ['CVSS v4.x Score'] = Cvssv4xScore,\n    ['EPSS Score'] = EpssScore,\n    ['CISA KEV Due Date'] = CisaKnownExploitedDueDate,\n    ['Executive Summary'] = ExecSummary,\n    ['Description'] = Description\n| project TimeGenerated, ['CVE ID'], ['Name'], ['Priority'], ['Risk Rating'], ['Predicted Risk Rating'], ['Exploitation State'], ['CVSS v3.x Score'], ['CVSS v4.x Score'], ['EPSS Score'], ['CISA KEV Due Date'], ['Executive Summary'], ['Description']",
                    "size": 4,
                    "showAnalytics": true,
                    "title": "Latest Vulnerability Intelligence For : {CveId}",
                    "noDataMessage": "Refresh panel if no data is available.",
                    "timeContextFromParameter": "TimeRange",
                    "showRefreshButton": true,
                    "queryType": 0,
                    "resourceType": "microsoft.operationalinsights/workspaces",
                    "visualization": "table"
                  },
                  "name": "query - vuln - latest",
                  "styleSettings": {
                    "showBorder": true
                  }
                }
              ]
            },
            "conditionalVisibilities": [
              {
                "parameterName": "status",
                "comparison": "isEqualTo",
                "value": "Click here to populate data."
              }
            ],
            "name": "group - vuln - latest"
          }
        ]
      },
      "conditionalVisibility": {
        "parameterName": "dashboard",
        "comparison": "isEqualTo",
        "value": "vuln_enrichment"
      },
      "name": "group - vuln_enrichment"
    },
    {
      "type": 12,
      "content": {
        "version": "NotebookGroup/1.0",
        "groupType": "editable",
        "items": [
          {
            "type": 1,
            "content": {
              "json": "## Private URL Scanning"
            },
            "name": "text - url - title"
          },
          {
            "type": 1,
            "content": {
              "json": "This page depends on the **GTIURLScanEnrichment** playbook which is deployed with the Microsoft Sentinel Solution.</br>\nPlease deploy this playbook first and keep it enabled in order to use this page.",
              "style": "info"
            },
            "name": "text - url - info"
          },
          {
            "type": 1,
            "content": {
              "json": "## Private URL Scanning Workbook\n\n**Purpose**: Submit a URL to the **GTIURLScanEnrichment** playbook for private scanning against Google Threat Intelligence and review the verdict (severity, threat score, safebrowsing verdict), right from within Microsoft Sentinel.\n\n### Setup (3 steps)\n1. **Select Environment**: Choose the **Subscription ID** and **Resource Group** where the GTIURLScanEnrichment playbook is deployed.\n2. **Enter URL**: Type the URL to submit for scanning, e.g. `http://example.com`.\n3. **Scan Options** (all optional, forwarded to GTI's private URL scanning API, see [GTI url-scan-private reference](https://gtidocs.virustotal.com/reference/private-scan-url)):\n   - **User Agent**: leave blank to use GTI's own default user agent, or provide a custom user agent string to submit with the scan request.\n   - **Storage Region**: leave at `Default` to use your account's `private_scanning.storage_region` preference, or pick `US`/`CA`/`EU`/`GB` to store the scanned URL in a specific region for data residency requirements.\n\n### Run Scan\n1. Click **\"Scan URL\"**.\n2. In the confirmation panel, review the details (including the scan options selected above) and click **\"Scan URL\"** to confirm.\n3. The GTIURLScanEnrichment Logic App submits the URL to GTI with the selected options, polls the analysis with progressive back-off (30s→60s→120s, up to 30 minutes), and ingests the full report into the `GTI_URLScan_CL` table once complete.\n\n### View Results\n1. Wait a few moments for the scan and ingestion to complete, then click the refresh icon on the status tile below.\n2. Once the tile displays **\"Click here to populate data.\"**, click it (or click Refresh on the panel) to load the **Latest Scan Result** Information for that URL.\n3. The **Private URL Scan Results** grid further below always shows the full scan history for all URLs within the selected Time Range.\n\n### Important Notes\n- **Scan Duration**: Analysis can take several minutes; the status tile will keep showing \"Click on refresh icon to check data availability\" until it completes — refresh again after a short wait.\n- **Partial Match**: The status tile and latest-result lookup match on any URL containing the value you entered, so `example.com` will also match `http://example.com`.\n- **Progress Tracking**: Monitor the GTIURLScanEnrichment Logic App run history in the Azure Portal if data doesn't appear after several minutes.\n\n### Special privileges required\nPrivate Scanning endpoints are only available to users with a Private Scanning license. https://www.virustotal.com/gui/private-scanning-overview",
              "style": "upsell"
            },
            "name": "text - url - howto"
          },
          {
            "type": 9,
            "content": {
              "version": "KqlParameterItem/1.0",
              "parameters": [
                {
                  "id": "651a640e-cb56-4a0f-a34f-177c893fc42a",
                  "version": "KqlParameterItem/1.0",
                  "name": "TimeRange",
                  "label": "Time Range",
                  "type": 4,
                  "description": "Time window applied to the Private URL Scan Results grid and the status/latest-result lookups below. Does not affect the playbook run itself.",
                  "isRequired": true,
                  "typeSettings": {
                    "selectableValues": [
                      {
                        "durationMs": 300000
                      },
                      {
                        "durationMs": 900000
                      },
                      {
                        "durationMs": 1800000
                      },
                      {
                        "durationMs": 3600000
                      },
                      {
                        "durationMs": 14400000
                      },
                      {
                        "durationMs": 43200000
                      },
                      {
                        "durationMs": 86400000
                      },
                      {
                        "durationMs": 172800000
                      },
                      {
                        "durationMs": 259200000
                      },
                      {
                        "durationMs": 604800000
                      },
                      {
                        "durationMs": 2592000000
                      },
                      {
                        "durationMs": 7776000000
                      }
                    ]
                  },
                  "value": {
                    "durationMs": 3600000
                  }
                },
                {
                  "id": "652d9893-a47e-4b17-851b-d3d0e18c8a79",
                  "version": "KqlParameterItem/1.0",
                  "name": "SubscriptionId",
                  "label": "Subscription ID",
                  "type": 2,
                  "description": "Select the Azure Subscription where the GTIURLScanEnrichment playbook (Logic App) is deployed.",
                  "isRequired": true,
                  "query": "Resources\n| distinct subscriptionId",
                  "typeSettings": {
                    "showDefault": false
                  },
                  "timeContext": {
                    "durationMs": 86400000
                  },
                  "queryType": 1,
                  "resourceType": "microsoft.resourcegraph/resources",
                  "value": null
                },
                {
                  "id": "d8d01e5b-7240-432c-838d-1db24531aa1c",
                  "version": "KqlParameterItem/1.0",
                  "name": "ResourceGroup",
                  "label": "Resource Group",
                  "type": 2,
                  "description": "Select the Resource Group where the GTIURLScanEnrichment playbook (Logic App) is deployed.",
                  "isRequired": true,
                  "query": "Resources\n| extend ResourceGroupName = resourceGroup\n| distinct ResourceGroupName",
                  "typeSettings": {
                    "showDefault": false
                  },
                  "timeContext": {
                    "durationMs": 86400000
                  },
                  "queryType": 1,
                  "resourceType": "microsoft.resourcegraph/resources",
                  "value": null
                },
                {
                  "id": "72a8f1f7-8981-4fbb-9431-f42e01bf947d",
                  "version": "KqlParameterItem/1.0",
                  "name": "ScanUrl",
                  "label": "URL",
                  "type": 1,
                  "description": "Enter the URL to submit for private scanning",
                  "isRequired": true,
                  "timeContext": {
                    "durationMs": 86400000
                  }
                },
                {
                  "id": "8b1e3f2a-6c4d-4a1e-9f3b-2d5a7c9e1b6f",
                  "version": "KqlParameterItem/1.0",
                  "name": "UserAgent",
                  "label": "User Agent (optional)",
                  "type": 1,
                  "description": "GTI's user_agent parameter. Optional user agent string submitted with the scan request. Leave blank to use GTI's own default.",
                  "isRequired": false,
                  "timeContext": {
                    "durationMs": 86400000
                  }
                },
                {
                  "id": "3d7c9a1e-5b8f-4e2d-8a6c-1f4b9d3e7c2a",
                  "version": "KqlParameterItem/1.0",
                  "name": "StorageRegion",
                  "label": "Storage Region",
                  "type": 2,
                  "description": "GTI's storage_region parameter (optional). Geographic region where Google Threat Intelligence stores the scanned URL for this analysis. Leave as default to use your account's private_scanning.storage_region preference.",
                  "typeSettings": {
                    "additionalResourceOptions": [],
                    "showDefault": false
                  },
                  "jsonData": "[\n  { \"value\": \"\", \"label\": \"Default (use account preference)\" },\n  { \"value\": \"US\", \"label\": \"United States (US)\" },\n  { \"value\": \"CA\", \"label\": \"Canada (CA)\" },\n  { \"value\": \"EU\", \"label\": \"European Union (EU)\" },\n  { \"value\": \"GB\", \"label\": \"United Kingdom (GB)\" }\n]",
                  "value": null
                }
              ],
              "style": "pills",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces"
            },
            "name": "parameters - url"
          },
          {
            "type": 11,
            "content": {
              "version": "LinkItem/1.0",
              "style": "paragraph",
              "links": [
                {
                  "id": "abbe6555-84ce-4033-bb09-297d13752ec3",
                  "cellValue": "",
                  "linkTarget": "ArmAction",
                  "linkLabel": "Scan URL",
                  "style": "primary",
                  "linkIsContextBlade": true,
                  "armActionContext": {
                    "path": "/subscriptions/{SubscriptionId}/resourceGroups/{ResourceGroup}/providers/Microsoft.Logic/workflows/GTIURLScanEnrichment/triggers/manual/run?api-version=2016-10-01",
                    "body": "{\n  \"url\": \"{ScanUrl}\",\n  \"user_agent\": \"{UserAgent}\",\n  \"storage_region\": \"{StorageRegion}\"\n}",
                    "httpMethod": "POST",
                    "description": "This will submit the URL to Google Threat Intelligence for private scanning. The playbook may take several minutes to complete. user_agent and storage_region are forwarded to GTI's private scanning API as submitted above; leave User Agent / Storage Region at their default (blank) to use GTI's own defaults.",
                    "actionName": "Playbook Trigger",
                    "runLabel": "Scan URL"
                  }
                }
              ]
            },
            "conditionalVisibilities": [
              {
                "parameterName": "SubscriptionId",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "ResourceGroup",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "ScanUrl",
                "comparison": "isNotEqualTo"
              }
            ],
            "name": "links - url - submit"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "let dummy_table = datatable(TimeGenerated: datetime, attributes_url: string) [];\nunion isfuzzy=true dummy_table,\n(GTI_URLScan_CL\n| extend attributes_url = tostring(column_ifexists(\"attributes_url\", \"\"))\n| where attributes_url contains '{ScanUrl}')\n| summarize count()\n| extend status = case(count_ == 0, \"Click on refresh icon to check data availability\", \"Click here to populate data.\")\n| project status",
              "size": 3,
              "timeContextFromParameter": "TimeRange",
              "showRefreshButton": true,
              "exportFieldName": "status",
              "exportParameterName": "status",
              "exportDefaultValue": "",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "visualization": "tiles",
              "tileSettings": {
                "titleContent": {
                  "columnMatch": "status"
                },
                "showBorder": false,
                "size": "full"
              }
            },
            "customWidth": "30",
            "conditionalVisibilities": [
              {
                "parameterName": "SubscriptionId",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "ResourceGroup",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "ScanUrl",
                "comparison": "isNotEqualTo"
              }
            ],
            "name": "query - url - status",
            "styleSettings": {
              "margin": "28px"
            }
          },
          {
            "type": 12,
            "content": {
              "version": "NotebookGroup/1.0",
              "groupType": "editable",
              "items": [
                {
                  "type": 3,
                  "content": {
                    "version": "KqlItem/1.0",
                    "query": "let dummy_table = datatable(TimeGenerated: datetime, attributes_url: string) [];\nlet mainTable = union isfuzzy=true dummy_table, GTI_URLScan_CL\n| extend attributes_url = tostring(column_ifexists(\"attributes_url\", \"\"))\n| where attributes_url contains '{ScanUrl}'\n| top 1 by TimeGenerated desc;\nmainTable\n| extend\n    attributes_url = tostring(column_ifexists(\"attributes_url\", \"\")),\n    attributes_last_final_url = tostring(column_ifexists(\"attributes_last_final_url\", \"\")),\n    attributes_title = tostring(column_ifexists(\"attributes_title\", \"\")),\n    attributes_gti_assessment_verdict_value = tostring(column_ifexists(\"attributes_gti_assessment_verdict_value\", \"\")),\n    attributes_gti_assessment_severity_value = tostring(column_ifexists(\"attributes_gti_assessment_severity_value\", \"\")),\n    attributes_gti_assessment_threat_score_value = toreal(column_ifexists(\"attributes_gti_assessment_threat_score_value\", real(null))),\n    attributes_last_http_response_code = toreal(column_ifexists(\"attributes_last_http_response_code\", real(null))),\n    attributes_gti_cf_safebrowsing_verdict = tostring(column_ifexists(\"attributes_gti_cf_safebrowsing_verdict\", \"\")),\n    attributes_gti_assessment_description = tostring(column_ifexists(\"attributes_gti_assessment_description\", \"\"))\n| summarize arg_max(TimeGenerated, *) by attributes_url\n| extend\n    ['URL'] = attributes_url,\n    ['Final URL'] = attributes_last_final_url,\n    ['Title'] = attributes_title,\n    ['Verdict'] = attributes_gti_assessment_verdict_value,\n    ['Severity'] = attributes_gti_assessment_severity_value,\n    ['Threat Score'] = attributes_gti_assessment_threat_score_value,\n    ['Response Code'] = attributes_last_http_response_code,\n    ['Safebrowsing Verdict'] = attributes_gti_cf_safebrowsing_verdict,\n    ['Description'] = attributes_gti_assessment_description\n| project TimeGenerated, ['URL'], ['Final URL'], ['Title'], ['Verdict'], ['Severity'], ['Threat Score'], ['Response Code'], ['Safebrowsing Verdict'], ['Description']",
                    "size": 4,
                    "showAnalytics": true,
                    "title": "Latest Scan Result For : {ScanUrl}",
                    "noDataMessage": "Refresh panel if no data is available.",
                    "timeContextFromParameter": "TimeRange",
                    "showRefreshButton": true,
                    "queryType": 0,
                    "resourceType": "microsoft.operationalinsights/workspaces",
                    "visualization": "table"
                  },
                  "name": "query - url - latest",
                  "styleSettings": {
                    "showBorder": true
                  }
                }
              ]
            },
            "conditionalVisibilities": [
              {
                "parameterName": "status",
                "comparison": "isEqualTo",
                "value": "Click here to populate data."
              }
            ],
            "name": "group - url - latest"
          }
        ]
      },
      "conditionalVisibility": {
        "parameterName": "dashboard",
        "comparison": "isEqualTo",
        "value": "url_scan"
      },
      "name": "group - url_scan"
    },
    {
      "type": 12,
      "content": {
        "version": "NotebookGroup/1.0",
        "groupType": "editable",
        "items": [
          {
            "type": 1,
            "content": {
              "json": "## Private File Scanning"
            },
            "name": "text - file - title"
          },
          {
            "type": 1,
            "content": {
              "json": "This page depends on the **GTIFileScanEnrichment** (on-demand) and **GTIFileScanBlobEnrichment** (automatic) playbooks which are deployed with the Microsoft Sentinel Solution.</br>\nPlease deploy the relevant playbook(s) first and keep them enabled in order to use this page.",
              "style": "info"
            },
            "name": "text - file - info"
          },
          {
            "type": 1,
            "content": {
              "json": "## Private File Scanning Workbook\n\n**Purpose**: Submit a file stored in Azure Blob Storage to the **GTIFileScanEnrichment** playbook for private scanning against Google Threat Intelligence and review the verdict (threat verdict, severity, type, size), right from within Microsoft Sentinel. Files can also be scanned automatically when added to a monitored container, via the **GTIFileScanBlobEnrichment** playbook.\n\n### Setup (3 steps)\n1. **Select Environment**: Choose the **Subscription ID** and **Resource Group** where the GTIFileScanEnrichment playbook is deployed.\n2. **Enter File Location**: Type the **Storage Account Name**, **Container Name**, and **Blob Path** (folder/file, e.g. `ScanFile/sheet.txt`) of the file to scan.\n3. **Scan Options** (all optional, forwarded to GTI's private scanning API, see [GTI upload-file-private-scanning reference](https://gtidocs.virustotal.com/reference/upload-file-private-scanning)):\n   - **Disable Sandbox**: leave at `Default` to use GTI's own default (sandbox detonation enabled); pick `Yes` to skip sandbox detonation and only perform static analysis, or `No` to force sandbox detonation.\n   - **Storage Region**: leave at `Default` to use your account's `private_scanning.storage_region` preference, or pick `US`/`CA`/`EU`/`GB` to store the scanned file in a specific region for data residency requirements.\n   - **ZIP Password (optional)**: only needed if the file is a password-protected ZIP archive that GTI must decompress before scanning. Leave blank for regular files.\n\n### Run Scan\n1. Click **\"Scan File\"**.\n2. In the confirmation panel, review the details (including the scan options selected above) and click **\"Scan File\"** to confirm.\n3. The GTIFileScanEnrichment Logic App retrieves the blob, submits it to GTI for scanning with the selected options (handling both standard and large files >32 MB), polls the analysis until complete, and ingests the full report into the `GTI_FileScan_CL` table.\n\n### View Results\n1. Wait a few moments for the scan and ingestion to complete, then click the refresh icon on the status tile below.\n2. Once the tile displays **\"Click here to populate data.\"**, click it (or click Refresh on the panel) to load the **Latest Scan Result** Information for that file.\n3. The **Private File Scan Results** grid below lists every file scan ingested into `GTI_FileScan_CL` within the selected time range (defaults to the last 24 hours), most recent first — this includes results from both the on-demand **GTIFileScanEnrichment** playbook and the automatic **GTIFileScanBlobEnrichment** playbook, since both write to the same table.\n\n### Automatic Scanning\n- Any file added to or modified in a container monitored by the **GTIFileScanBlobEnrichment** playbook is scanned automatically on the same schedule, with results ingested into the same `GTI_FileScan_CL` table — no manual submission needed for those files.\n\n### Important Notes\n- **Scan Duration**: Analysis can take several minutes, longer for large files; the status tile will keep showing \"Click on refresh icon to check data availability\" until it completes — refresh again after a short wait.\n- **File Name Match**: The status tile and latest-result lookup match on the file name portion of the Blob Path you entered (e.g. `sheet.txt`), not the full path, so the same file name in a different folder will also match.\n- **Time Range**: The status tile and latest-result lookup only search within the selected **Time Range** above the parameters — widen it if a recent scan result isn't showing up.\n- **Sandbox Detonation**: Disabling the sandbox speeds up analysis but skips dynamic/behavioral detection, relying on static analysis only.\n- **Progress Tracking**: Monitor the GTIFileScanEnrichment / GTIFileScanBlobEnrichment Logic App run history in the Azure Portal if data doesn't appear after several minutes.\n\n### Special privileges required\nPrivate Scanning endpoints are only available to users with a Private Scanning license. https://www.virustotal.com/gui/private-scanning-overview",
              "style": "upsell"
            },
            "name": "text - file - howto"
          },
          {
            "type": 9,
            "content": {
              "version": "KqlParameterItem/1.0",
              "parameters": [
                {
                  "id": "706560d8-9cb1-4bf1-a581-91e7157ce228",
                  "version": "KqlParameterItem/1.0",
                  "name": "TimeRange",
                  "label": "Time Range",
                  "type": 4,
                  "description": "Time window applied to the status/latest-result lookups and results grid below. Does not affect the playbook run itself.",
                  "isRequired": true,
                  "typeSettings": {
                    "selectableValues": [
                      {
                        "durationMs": 300000
                      },
                      {
                        "durationMs": 900000
                      },
                      {
                        "durationMs": 1800000
                      },
                      {
                        "durationMs": 3600000
                      },
                      {
                        "durationMs": 14400000
                      },
                      {
                        "durationMs": 43200000
                      },
                      {
                        "durationMs": 86400000
                      },
                      {
                        "durationMs": 172800000
                      },
                      {
                        "durationMs": 259200000
                      },
                      {
                        "durationMs": 604800000
                      },
                      {
                        "durationMs": 2592000000
                      },
                      {
                        "durationMs": 7776000000
                      }
                    ]
                  },
                  "value": {
                    "durationMs": 86400000
                  }
                },
                {
                  "id": "971cd36b-548d-42cb-a6a5-959ba23c2aaa",
                  "version": "KqlParameterItem/1.0",
                  "name": "SubscriptionId",
                  "label": "Subscription ID",
                  "type": 2,
                  "description": "Select the Azure Subscription where the GTIFileScanEnrichment playbook (Logic App) is deployed.",
                  "isRequired": true,
                  "query": "Resources\n| distinct subscriptionId",
                  "typeSettings": {
                    "showDefault": false
                  },
                  "timeContext": {
                    "durationMs": 86400000
                  },
                  "queryType": 1,
                  "resourceType": "microsoft.resourcegraph/resources",
                  "value": null
                },
                {
                  "id": "4967281c-c865-4169-ba63-0b2f21c79737",
                  "version": "KqlParameterItem/1.0",
                  "name": "ResourceGroup",
                  "label": "Resource Group",
                  "type": 2,
                  "description": "Select the Resource Group where the GTIFileScanEnrichment playbook (Logic App) is deployed.",
                  "isRequired": true,
                  "query": "Resources\n| extend ResourceGroupName = resourceGroup\n| distinct ResourceGroupName",
                  "typeSettings": {
                    "showDefault": false
                  },
                  "timeContext": {
                    "durationMs": 86400000
                  },
                  "queryType": 1,
                  "resourceType": "microsoft.resourcegraph/resources",
                  "value": null
                },
                {
                  "id": "c79fef1d-4038-4142-807e-b2f47a2ad4c1",
                  "version": "KqlParameterItem/1.0",
                  "name": "StorageAccountName",
                  "label": "Storage Account Name",
                  "type": 1,
                  "description": "Name of the Azure Storage Account containing the file",
                  "isRequired": true,
                  "timeContext": {
                    "durationMs": 86400000
                  }
                },
                {
                  "id": "4591212d-7f4d-4d28-9188-723b88668402",
                  "version": "KqlParameterItem/1.0",
                  "name": "ContainerName",
                  "label": "Container Name",
                  "type": 1,
                  "description": "Name of the blob container, e.g. VulnerabilityData",
                  "isRequired": true,
                  "timeContext": {
                    "durationMs": 86400000
                  }
                },
                {
                  "id": "519e50e3-f2dd-4f27-82f4-64ad2e38271e",
                  "version": "KqlParameterItem/1.0",
                  "name": "BlobPath",
                  "label": "Blob Path",
                  "type": 1,
                  "description": "Path of the blob within the container, e.g. folder/file.exe",
                  "isRequired": true,
                  "timeContext": {
                    "durationMs": 86400000
                  }
                },
                {
                  "id": "41a489c6-f66b-4129-9e8a-6090a3c19a00",
                  "version": "KqlParameterItem/1.0",
                  "name": "DisableSandbox",
                  "label": "Disable Sandbox",
                  "type": 2,
                  "description": "GTI's disable_sandbox parameter (optional). If Yes, the file will not be detonated in any sandbox environment during private scanning. Leave as default to use GTI's own default (sandbox detonation enabled).",
                  "typeSettings": {
                    "additionalResourceOptions": [],
                    "showDefault": false
                  },
                  "jsonData": "[\n    {\n        \"value\": \"\",\n        \"label\": \"Default (GTI default - sandbox detonation enabled)\"\n    },\n    {\n        \"value\": \"true\",\n        \"label\": \"Yes - skip sandbox detonation\"\n    },\n    {\n        \"value\": \"false\",\n        \"label\": \"No - detonate the file in sandbox environments\"\n    }\n]",
                  "value": null
                },
                {
                  "id": "70039e9c-bd9e-487b-8065-5c537d14d718",
                  "version": "KqlParameterItem/1.0",
                  "name": "StorageRegion",
                  "label": "Storage Region",
                  "type": 2,
                  "description": "GTI's storage_region parameter (optional). Geographic region where Google Threat Intelligence stores the scanned file for this analysis. Leave as default to use your account's private_scanning.storage_region preference.",
                  "typeSettings": {
                    "additionalResourceOptions": [],
                    "showDefault": false
                  },
                  "jsonData": "[\n  { \"value\": \"\", \"label\": \"Default (use account preference)\" },\n  { \"value\": \"US\", \"label\": \"United States (US)\" },\n  { \"value\": \"CA\", \"label\": \"Canada (CA)\" },\n  { \"value\": \"EU\", \"label\": \"European Union (EU)\" },\n  { \"value\": \"GB\", \"label\": \"United Kingdom (GB)\" }\n]",
                  "value": null
                },
                {
                  "id": "6e0e5a78-d305-407e-b196-cd6970531ac5",
                  "version": "KqlParameterItem/1.0",
                  "name": "Password",
                  "label": "ZIP Password (optional)",
                  "type": 1,
                  "description": "GTI's password parameter. Optional password used to decompress and scan a file contained in a password-protected ZIP archive. Leave blank if the file is not a password-protected ZIP.",
                  "isRequired": false,
                  "timeContext": {
                    "durationMs": 86400000
                  }
                }
              ],
              "style": "pills",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces"
            },
            "name": "parameters - file"
          },
          {
            "type": 11,
            "content": {
              "version": "LinkItem/1.0",
              "style": "paragraph",
              "links": [
                {
                  "id": "c096fb73-5cb0-4c98-b167-36c9a38caaa3",
                  "cellValue": "",
                  "linkTarget": "ArmAction",
                  "linkLabel": "Scan File",
                  "style": "primary",
                  "linkIsContextBlade": true,
                  "armActionContext": {
                    "path": "/subscriptions/{SubscriptionId}/resourceGroups/{ResourceGroup}/providers/Microsoft.Logic/workflows/GTIFileScanEnrichment/triggers/manual/run?api-version=2016-10-01",
                    "body": "{\n  \"storageAccountName\": \"{StorageAccountName}\",\n  \"containerName\": \"{ContainerName}\",\n  \"blobPath\": \"{BlobPath}\",\n  \"disable_sandbox\": \"{DisableSandbox}\",\n  \"storage_region\": \"{StorageRegion}\",\n  \"password\": \"{Password}\"\n}",
                    "httpMethod": "POST",
                    "description": "This will retrieve the specified blob and submit it to Google Threat Intelligence for private scanning. The playbook may take several minutes to complete. disable_sandbox, storage_region, and password are forwarded to GTI's private scanning API as submitted above; leave Disable Sandbox / Storage Region at their default (blank) to use GTI's own defaults.",
                    "actionName": "Playbook Trigger",
                    "runLabel": "Scan File"
                  }
                }
              ]
            },
            "conditionalVisibilities": [
              {
                "parameterName": "SubscriptionId",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "ResourceGroup",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "StorageAccountName",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "ContainerName",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "BlobPath",
                "comparison": "isNotEqualTo"
              }
            ],
            "name": "links - file - submit"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "let dummy_table = datatable(TimeGenerated: datetime, attributes_names: dynamic) [];\nlet ExpectedFileName = tostring(split('{BlobPath}', '/')[-1]);\nunion isfuzzy=true dummy_table,\n(GTI_FileScan_CL\n| where tostring(attributes_names) contains ExpectedFileName)\n| summarize count()\n| extend status = case(count_ == 0, \"Click on refresh icon to check data availability\", \"Click here to populate data.\")\n| project status",
              "size": 3,
              "timeContextFromParameter": "TimeRange",
              "showRefreshButton": true,
              "exportFieldName": "status",
              "exportParameterName": "status",
              "exportDefaultValue": "",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "visualization": "tiles",
              "tileSettings": {
                "titleContent": {
                  "columnMatch": "status"
                },
                "showBorder": false,
                "size": "full"
              }
            },
            "customWidth": "30",
            "conditionalVisibilities": [
              {
                "parameterName": "SubscriptionId",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "ResourceGroup",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "StorageAccountName",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "ContainerName",
                "comparison": "isNotEqualTo"
              },
              {
                "parameterName": "BlobPath",
                "comparison": "isNotEqualTo"
              }
            ],
            "name": "query - file - status",
            "styleSettings": {
              "margin": "28px"
            }
          },
          {
            "type": 12,
            "content": {
              "version": "NotebookGroup/1.0",
              "groupType": "editable",
              "items": [
                {
                  "type": 3,
                  "content": {
                    "version": "KqlItem/1.0",
                    "query": "let ExpectedFileName = tostring(split('{BlobPath}', '/')[-1]);\nGTI_FileScan_CL\n| where tostring(attributes_names) contains ExpectedFileName\n| top 1 by TimeGenerated desc\n| extend\n    ['File Name'] = tostring(attributes_names[0]),\n    ['SHA256'] = attributes_sha256,\n    ['Verdict'] = attributes_threat_verdict,\n    ['Severity'] = attributes_gti_assessment_severity_value,\n    ['Type'] = attributes_type_description,\n    ['File Size'] = attributes_size\n| project TimeGenerated, ['File Name'], ['SHA256'], ['Verdict'], ['Severity'], ['Type'], ['File Size']",
                    "size": 4,
                    "showAnalytics": true,
                    "title": "Latest Scan Result For : {BlobPath}",
                    "noDataMessage": "Refresh panel if no data is available.",
                    "timeContextFromParameter": "TimeRange",
                    "showRefreshButton": true,
                    "queryType": 0,
                    "resourceType": "microsoft.operationalinsights/workspaces",
                    "visualization": "table"
                  },
                  "name": "query - file - latest",
                  "styleSettings": {
                    "showBorder": true
                  }
                }
              ]
            },
            "conditionalVisibilities": [
              {
                "parameterName": "status",
                "comparison": "isEqualTo",
                "value": "Click here to populate data."
              }
            ],
            "name": "group - file - latest"
          },
          {
            "type": 1,
            "content": {
              "json": "The grid below lists all private file scan results ingested within the selected time range, most recent first.",
              "style": "info"
            },
            "name": "text - file - tooltip"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "let dummy_table = datatable(TimeGenerated: datetime, attributes_names: dynamic, attributes_sha256: string, attributes_threat_verdict: string, attributes_gti_assessment_severity_value: string, attributes_type_description: string, attributes_size: real) [];\nunion isfuzzy=true dummy_table,\n(GTI_FileScan_CL\n| extend\n    attributes_names = column_ifexists(\"attributes_names\", dynamic(null)),\n    attributes_sha256 = tostring(column_ifexists(\"attributes_sha256\", \"\")),\n    attributes_threat_verdict = tostring(column_ifexists(\"attributes_threat_verdict\", \"\")),\n    attributes_gti_assessment_severity_value = tostring(column_ifexists(\"attributes_gti_assessment_severity_value\", \"\")),\n    attributes_type_description = tostring(column_ifexists(\"attributes_type_description\", \"\")),\n    attributes_size = column_ifexists(\"attributes_size\", real(null)))\n| extend\n    ['File Name'] = tostring(attributes_names[0]),\n    ['SHA256'] = attributes_sha256,\n    ['Verdict'] = attributes_threat_verdict,\n    ['Severity'] = attributes_gti_assessment_severity_value,\n    ['Type'] = attributes_type_description,\n    ['File Size'] = attributes_size\n| project TimeGenerated, ['File Name'], ['SHA256'], ['Verdict'], ['Severity'], ['Type'], ['File Size']",
              "size": 0,
              "showAnalytics": true,
              "title": "Private File Scan Results",
              "noDataMessage": "No file scan data yet. Submit a file above, then click Refresh.",
              "timeContextFromParameter": "TimeRange",
              "showExportToExcel": true,
              "showRefreshButton": true,
              "showOpenInEditor": true,
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "visualization": "table",
              "gridSettings": {
                "formatters": [
                  {
                    "columnMatch": "Verdict",
                    "formatter": 18,
                    "formatOptions": {
                      "thresholdsOptions": "icons",
                      "thresholdsGrid": [
                        {
                          "operator": "==",
                          "thresholdValue": "VERDICT_MALICIOUS",
                          "representation": "criticalError",
                          "text": "{0}{1}"
                        },
                        {
                          "operator": "==",
                          "thresholdValue": "VERDICT_SUSPICIOUS",
                          "representation": "warning",
                          "text": "{0}{1}"
                        },
                        {
                          "operator": "Default",
                          "representation": "success",
                          "text": "{0}{1}"
                        }
                      ]
                    }
                  },
                  {
                    "columnMatch": "Trigger Type",
                    "formatter": 18,
                    "formatOptions": {
                      "thresholdsOptions": "icons",
                      "thresholdsGrid": [
                        {
                          "operator": "==",
                          "thresholdValue": "BlobTrigger",
                          "representation": "info",
                          "text": "{0}{1}"
                        },
                        {
                          "operator": "Default",
                          "representation": "success",
                          "text": "{0}{1}"
                        }
                      ]
                    }
                  }
                ],
                "rowLimit": 10000,
                "filter": true,
                "sortBy": [
                  {
                    "itemKey": "TimeGenerated",
                    "sortOrder": 2
                  }
                ]
              },
              "sortBy": [
                {
                  "itemKey": "TimeGenerated",
                  "sortOrder": 2
                }
              ],
              "exportOptions": {
                "csvDownloadFileName": "GTI_Private_File_Scan"
              }
            },
            "name": "query - file - grid"
          }
        ]
      },
      "conditionalVisibility": {
        "parameterName": "dashboard",
        "comparison": "isEqualTo",
        "value": "file_scan"
      },
      "name": "group - file_scan"
    }
  ],
  "fromTemplateId": "sentinel-GoogleThreatIntelligence",
  "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json"
}