diff --git a/.github/workflows/check-license-headers.yml b/.github/workflows/check-license-headers.yml
new file mode 100644
index 0000000..22621c1
--- /dev/null
+++ b/.github/workflows/check-license-headers.yml
@@ -0,0 +1,80 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+name: Check License Headers
+
+on:
+ pull_request:
+ push:
+ branches: [main]
+ workflow_dispatch:
+
+jobs:
+ check-license:
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v5
+
+ - name: Check license headers
+ run: |
+ echo "Checking for required license headers..."
+
+ # Define the required headers for different file types
+ HEADER_HASH="# Copyright (c) Microsoft Corporation."
+ HEADER_SLASH="// Copyright (c) Microsoft Corporation."
+
+ # Find files that should have headers
+ files_to_check=$(find . -type f \( \
+ -name "*.cs" -o \
+ -name "*.ts" -o \
+ -name "*.js" -o \
+ -name "*.sh" -o \
+ -name "*.yml" -o \
+ -name "*.yaml" \
+ \) \
+ -not -path "./.git/*" \
+ -not -path "./node_modules/*" \
+ -not -path "./**/bin/*" \
+ -not -path "./**/obj/*")
+
+ missing_headers=()
+
+ for file in $files_to_check; do
+ echo "Checking: $file"
+
+ # Check based on file extension
+ case "$file" in
+ *.sh|*.yml|*.yaml)
+ if ! head -5 "$file" | grep -q "# Copyright (c) Microsoft Corporation."; then
+ missing_headers+=("$file")
+ fi
+ ;;
+ *.cs|*.ts|*.js)
+ if ! head -5 "$file" | grep -q "// Copyright (c) Microsoft Corporation."; then
+ missing_headers+=("$file")
+ fi
+ ;;
+ esac
+ done
+
+ # Report results
+ if [ ${#missing_headers[@]} -eq 0 ]; then
+ echo "✅ All files have required license headers"
+ else
+ echo "❌ The following files are missing license headers:"
+ printf '%s\n' "${missing_headers[@]}"
+ echo ""
+ echo "Please add the appropriate header to each file:"
+ echo "For .sh/.yml/.yaml files:"
+ echo " # Copyright (c) Microsoft Corporation."
+ echo " # Licensed under the MIT License."
+ echo ""
+ echo "For .cs/.ts/.js files:"
+ echo " // Copyright (c) Microsoft Corporation."
+ echo " // Licensed under the MIT License."
+ exit 1
+ fi
\ No newline at end of file
diff --git a/.github/workflows/update-ip-ranges.yml b/.github/workflows/update-ip-ranges.yml
new file mode 100644
index 0000000..db80b69
--- /dev/null
+++ b/.github/workflows/update-ip-ranges.yml
@@ -0,0 +1,55 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+name: Verify IP Address Ranges
+
+on:
+ pull_request:
+ paths:
+ - 'config/IPAddressRanges.json'
+ - 'nodejs/config/IPAddressRanges.ts'
+ - 'csharp/config/IPAddressRanges.cs'
+ push:
+ branches: [main]
+ paths:
+ - 'config/IPAddressRanges.json'
+ - 'nodejs/config/IPAddressRanges.ts'
+ - 'csharp/config/IPAddressRanges.cs'
+ workflow_dispatch: # Allow manual triggering
+
+jobs:
+ verify-ranges:
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v5
+
+ - name: Install jq
+ run: sudo apt-get update && sudo apt-get install -y jq
+
+ - name: Make scripts executable
+ run: chmod +x scripts/*.sh
+
+ - name: Build Node.js/TypeScript version
+ run: ./scripts/build-ip-ranges-nodejs.sh
+
+ - name: Build C# version
+ run: ./scripts/build-ip-ranges-cs.sh
+
+ - name: Verify generated files are up-to-date
+ run: |
+ if ! git diff --exit-code nodejs/config/IPAddressRanges.ts csharp/config/IPAddressRanges.cs; then
+ echo "❌ Generated IP address range files are out of sync!"
+ echo "The following files need to be regenerated:"
+ git diff --name-only nodejs/config/IPAddressRanges.ts csharp/config/IPAddressRanges.cs
+ echo ""
+ echo "Please run the following commands locally and commit the results:"
+ echo " ./scripts/build-ip-ranges-nodejs.sh"
+ echo " ./scripts/build-ip-ranges-cs.sh"
+ exit 1
+ else
+ echo "✅ All generated files are up-to-date"
+ fi
diff --git a/.gitignore b/.gitignore
index ce89292..83cde20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -416,3 +416,5 @@ FodyWeavers.xsd
*.msix
*.msm
*.msp
+
+.DS_Store
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..83e069e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,33 @@
+# Contributing to AntiSSRF
+
+Thank you for your interest in contributing to the AntiSSRF project! This guide will help you get started.
+
+## Development Setup
+
+1. Clone the repository
+2. Install dependencies for your target language (C# or Node.js)
+3. Make your changes
+4. Test thoroughly
+5. Submit a pull request
+
+## Updating IP Address Ranges
+
+The IP address ranges are maintained in [`config/IPAddressRanges.json`](config/IPAddressRanges.json) and automatically generated into language-specific files.
+
+### Prerequisites
+Install `jq`: `brew install jq` (macOS) or `sudo apt-get install jq` (Linux)
+
+### Process
+1. **Edit** [`config/IPAddressRanges.json`](config/IPAddressRanges.json)
+2. **Regenerate** the code files:
+ ```bash
+ ./scripts/build-ip-ranges-nodejs.sh
+ ./scripts/build-ip-ranges-cs.sh
+ ```
+3. **Commit** all changes (JSON + generated files):
+ ```bash
+ git add config/IPAddressRanges.json nodejs/config/IPAddressRanges.ts csharp/config/IPAddressRanges.cs
+ git commit -m "Update IP address ranges"
+ ```
+
+**Important**: The GitHub Actions workflow will fail if generated files don't match the source JSON.
diff --git a/config/IPAddressRanges.json b/config/IPAddressRanges.json
new file mode 100644
index 0000000..8ef68eb
--- /dev/null
+++ b/config/IPAddressRanges.json
@@ -0,0 +1,439 @@
+{
+ "_sources": {
+ "ianaIpv4": {
+ "title": "IANA IPv4 Special-Purpose Address Registry",
+ "url": "https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml",
+ "last_updated": "2025-10-09"
+ },
+ "ianaIpv6": {
+ "title": "IANA IPv6 Special-Purpose Address Registry",
+ "url": "https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml",
+ "last_updated": "2025-10-09"
+ },
+ "ianaIpv4Multicast": {
+ "title": "IANA IPv4 Multicast Address Space",
+ "url": "https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml",
+ "last_updated": "2026-01-27"
+ },
+ "ianaIpv6Multicast": {
+ "title": "IANA IPv6 Multicast Address Space",
+ "url": "https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml",
+ "last_updated": "2026-02-02"
+ },
+ "imds": {
+ "title": "Microsoft Learn Azure Instance Metadata Service",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service?tabs=windows",
+ "last_updated": "NA"
+ },
+ "wireserver": {
+ "title": "Microsoft Learn Azure IP Address 168.63.129.16 Overview",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-network/what-is-ip-address-168-63-129-16?tabs=windows",
+ "last_updated": "NA"
+ },
+ "siteLocal": {
+ "title": "Microsoft Learn IPv6 Link-local and Site-local Addresses",
+ "url": "https://learn.microsoft.com/en-us/windows/win32/winsock/link-local-and-site-local-addresses-2",
+ "last_updated": "NA"
+ }
+ },
+ "amt": {
+ "purpose": "AMT",
+ "cidr": [
+ "192.52.193.0/24",
+ "2001:3::/32"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "as112": {
+ "purpose": "AS112",
+ "cidr": [
+ "192.31.196.0/24",
+ "192.175.48.0/24",
+ "2001:4:112::/48",
+ "2620:4f:8000::/48"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "benchmarking": {
+ "purpose": "Benchmarking",
+ "cidr": [
+ "198.18.0.0/15",
+ "2001:2::/48"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "deprecated": {
+ "purpose": "Deprecated",
+ "cidr": [
+ "192.88.99.0/24",
+ "2001:10::/28"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "detsPrefix": {
+ "purpose": "Drone Remote ID Protocol Entity Tags (DETs) Prefix",
+ "cidr": [
+ "2001:30::/28"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv6"
+ ]
+ },
+ "discardOnly": {
+ "purpose": "Discard-Only Address Block",
+ "cidr": [
+ "100::/64"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv6"
+ ]
+ },
+ "dnsSdAnycast": {
+ "purpose": "DNS-SD Service Registration Protocol Anycast",
+ "cidr": [
+ "2001:1::3/128"
+ ],
+ "standaloneVariable": false,
+ "_sources": [
+ "ianaIpv6"
+ ]
+ },
+ "documentation": {
+ "purpose": "Documentation",
+ "cidr": [
+ "192.0.2.0/24",
+ "198.51.100.0/24",
+ "203.0.113.0/24",
+ "2001:db8::/32",
+ "3fff::/20"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "dummy": {
+ "purpose": "Dummy address",
+ "cidr": [
+ "192.0.0.8/32",
+ "100:0:0:1::/64"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "ietfProtocol": {
+ "purpose": "IETF Protocol Assignments",
+ "cidr": [
+ "192.0.0.0/24",
+ "2001::/23"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "imds": {
+ "purpose": "Instance Metadata Service",
+ "cidr": [
+ "169.254.169.254/32"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "imds"
+ ]
+ },
+ "ipv4Ipv6Translat": {
+ "purpose": "IPv4-IPv6 Translated",
+ "cidr": [
+ "64:ff9b::/96",
+ "64:ff9b:1::/48"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv6"
+ ]
+ },
+ "ipv4ServiceContinuity": {
+ "purpose": "IPv4 Service Continuity Prefix",
+ "cidr": [
+ "192.0.0.0/29"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4"
+ ]
+ },
+ "broadcast": {
+ "purpose": "Limited Broadcast",
+ "cidr": [
+ "255.255.255.255/32"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4"
+ ]
+ },
+ "linkLocal": {
+ "purpose": "Link Local",
+ "cidr": [
+ "169.254.0.0/16",
+ "fe80::/10"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6",
+ "siteLocal"
+ ]
+ },
+ "loopback": {
+ "purpose": "Loopback",
+ "cidr": [
+ "127.0.0.0/8",
+ "::1/128"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "multicast": {
+ "purpose": "Multicast",
+ "cidr": [
+ "224.0.0.0/4",
+ "ff00::/8"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4Multicast",
+ "ianaIpv6Multicast"
+ ]
+ },
+ "nat64Dns64Discovery": {
+ "purpose": "NAT64/DNS64 Discovery",
+ "cidr": [
+ "192.0.0.170/32",
+ "192.0.0.171/32"
+ ],
+ "standaloneVariable": false,
+ "_sources": [
+ "ianaIpv4"
+ ]
+ },
+ "orchidv2": {
+ "purpose": "ORCHIDv2",
+ "cidr": [
+ "2001:20::/28"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv6"
+ ]
+ },
+ "pcpAnycast": {
+ "purpose": "Port Control Protocol Anycast",
+ "cidr": [
+ "192.0.0.9/32",
+ "2001:1::1/128"
+ ],
+ "standaloneVariable": false,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "privateUse": {
+ "purpose": "Private-Use",
+ "cidr": [
+ "10.0.0.0/8",
+ "172.16.0.0/12",
+ "192.168.0.0/16"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4"
+ ]
+ },
+ "reserved": {
+ "purpose": "Reserved",
+ "cidr": [
+ "240.0.0.0/4"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4"
+ ]
+ },
+ "sharedAddressSpace": {
+ "purpose": "Shared Address Space",
+ "cidr": [
+ "100.64.0.0/10"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4"
+ ]
+ },
+ "siteLocal": {
+ "purpose": "Site Local",
+ "cidr": [
+ "fec0::/10"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "siteLocal"
+ ]
+ },
+ "six4aaRelayAnycast": {
+ "purpose": "6a44-relay Anycast Address",
+ "cidr": [
+ "192.88.99.2/32"
+ ],
+ "standaloneVariable": false,
+ "_sources": [
+ "ianaIpv4"
+ ]
+ },
+ "sixto4": {
+ "purpose": "6to4",
+ "cidr": [
+ "2002::/16"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv6"
+ ]
+ },
+ "srv6Sid": {
+ "purpose": "Segment Routing (SRv6) SIDs",
+ "cidr": [
+ "5f00::/16"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv6"
+ ]
+ },
+ "teredo": {
+ "purpose": "TEREDO",
+ "cidr": [
+ "2001::/32"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv6"
+ ]
+ },
+ "turnAnycast": {
+ "purpose": "Traversal Using Relays around NAT Anycast",
+ "cidr": [
+ "192.0.0.10/32",
+ "2001:1::2/128"
+ ],
+ "standaloneVariable": false,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "uniqueLocal": {
+ "purpose": "Unique Local Unicast",
+ "cidr": [
+ "fc00::/7"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv6"
+ ]
+ },
+ "unspecified": {
+ "purpose": "This host on this network; unspecified address",
+ "cidr": [
+ "0.0.0.0/8",
+ "::/128"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "ianaIpv4",
+ "ianaIpv6"
+ ]
+ },
+ "wireserver": {
+ "purpose": "Azure WireServer",
+ "cidr": [
+ "168.63.129.16/32"
+ ],
+ "standaloneVariable": true,
+ "_sources": [
+ "wireserver"
+ ]
+ },
+ "recommendedV1": {
+ "purpose": "Current list of recommended special-purpose addresses",
+ "cidr": [
+ "0.0.0.0/8",
+ "10.0.0.0/8",
+ "100.64.0.0/10",
+ "127.0.0.0/8",
+ "168.63.129.16/32",
+ "169.254.0.0/16",
+ "172.16.0.0/12",
+ "192.0.0.0/24",
+ "192.0.2.0/24",
+ "192.31.196.0/24",
+ "192.52.193.0/24",
+ "192.88.99.0/24",
+ "192.168.0.0/16",
+ "192.175.48.0/24",
+ "198.18.0.0/15",
+ "198.51.100.0/24",
+ "203.0.113.0/24",
+ "224.0.0.0/4",
+ "240.0.0.0/4",
+ "::1/128",
+ "::/128",
+ "64:ff9b::/96",
+ "64:ff9b:1::/48",
+ "100::/64",
+ "100:0:0:1::/64",
+ "2001::/23",
+ "2001:db8::/32",
+ "2002::/16",
+ "2620:4f:8000::/48",
+ "3fff::/20",
+ "5f00::/16",
+ "fc00::/7",
+ "fe80::/10",
+ "fec0::/10",
+ "ff00::/8"
+ ],
+ "standaloneVariable": true,
+ "_notes": [
+ "Skipping the following CIDR ranges that are already included in other CIDR ranges: 169.254.169.254/32, 192.0.0.0/29, 192.0.0.8/32, 192.0.0.9/32, 192.0.0.10/32, 192.0.0.170/32, 192.0.0.171/32, 192.88.99.2/32, 255.255.255.255/32, 2001::/32, 2001:1::1/128, 2001:1::2/128, 2001:1::3/128, 2001:2::/48, 2001:3::/32, 2001:4:112::/48, 2001:10::/28, 2001:20::/28, 2001:30::/28"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/csharp/config/IPAddressRanges.cs b/csharp/config/IPAddressRanges.cs
new file mode 100644
index 0000000..2b79bbb
--- /dev/null
+++ b/csharp/config/IPAddressRanges.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// Auto-generated from IPAddressRanges.json
+// Do not edit this file manually
+
+namespace Microsoft.Security.AntiSSRF
+{
+ ///
+ /// Static IP address ranges for AntiSSRF protection
+ ///
+ public static class IPAddressRanges
+ {
+ public static readonly string[] amt = { "192.52.193.0/24", "2001:3::/32" };
+ public static readonly string[] as112 = { "192.31.196.0/24", "192.175.48.0/24", "2001:4:112::/48", "2620:4f:8000::/48" };
+ public static readonly string[] benchmarking = { "198.18.0.0/15", "2001:2::/48" };
+ public static readonly string[] deprecated = { "192.88.99.0/24", "2001:10::/28" };
+ public static readonly string[] detsPrefix = { "2001:30::/28" };
+ public static readonly string[] discardOnly = { "100::/64" };
+ public static readonly string[] documentation = { "192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24", "2001:db8::/32", "3fff::/20" };
+ public static readonly string[] dummy = { "192.0.0.8/32", "100:0:0:1::/64" };
+ public static readonly string[] ietfProtocol = { "192.0.0.0/24", "2001::/23" };
+ public static readonly string[] imds = { "169.254.169.254/32" };
+ public static readonly string[] ipv4Ipv6Translat = { "64:ff9b::/96", "64:ff9b:1::/48" };
+ public static readonly string[] ipv4ServiceContinuity = { "192.0.0.0/29" };
+ public static readonly string[] broadcast = { "255.255.255.255/32" };
+ public static readonly string[] linkLocal = { "169.254.0.0/16", "fe80::/10" };
+ public static readonly string[] loopback = { "127.0.0.0/8", "::1/128" };
+ public static readonly string[] multicast = { "224.0.0.0/4", "ff00::/8" };
+ public static readonly string[] orchidv2 = { "2001:20::/28" };
+ public static readonly string[] privateUse = { "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" };
+ public static readonly string[] reserved = { "240.0.0.0/4" };
+ public static readonly string[] sharedAddressSpace = { "100.64.0.0/10" };
+ public static readonly string[] siteLocal = { "fec0::/10" };
+ public static readonly string[] sixto4 = { "2002::/16" };
+ public static readonly string[] srv6Sid = { "5f00::/16" };
+ public static readonly string[] teredo = { "2001::/32" };
+ public static readonly string[] uniqueLocal = { "fc00::/7" };
+ public static readonly string[] unspecified = { "0.0.0.0/8", "::/128" };
+ public static readonly string[] wireserver = { "168.63.129.16/32" };
+ public static readonly string[] recommendedV1 = { "0.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "168.63.129.16/32", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.88.99.0/24", "192.168.0.0/16", "192.175.48.0/24", "198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "224.0.0.0/4", "240.0.0.0/4", "::1/128", "::/128", "64:ff9b::/96", "64:ff9b:1::/48", "100::/64", "100:0:0:1::/64", "2001::/23", "2001:db8::/32", "2002::/16", "2620:4f:8000::/48", "3fff::/20", "5f00::/16", "fc00::/7", "fe80::/10", "fec0::/10", "ff00::/8" };
+
+ ///
+ /// recommendedLatest always points to the latest version
+ ///
+ public static readonly string[] recommendedLatest = recommendedV1;
+ }
+}
diff --git a/nodejs/config/IPAddressRanges.ts b/nodejs/config/IPAddressRanges.ts
new file mode 100644
index 0000000..130e9a9
--- /dev/null
+++ b/nodejs/config/IPAddressRanges.ts
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// Auto-generated from IPAddressRanges.json
+// Do not edit this file manually
+
+/**
+ * Static IP address ranges for AntiSSRF protection
+ */
+export class IPAddressRanges {
+ public static readonly amt: string[] = ["192.52.193.0/24", "2001:3::/32"];
+ public static readonly as112: string[] = ["192.31.196.0/24", "192.175.48.0/24", "2001:4:112::/48", "2620:4f:8000::/48"];
+ public static readonly benchmarking: string[] = ["198.18.0.0/15", "2001:2::/48"];
+ public static readonly deprecated: string[] = ["192.88.99.0/24", "2001:10::/28"];
+ public static readonly detsPrefix: string[] = ["2001:30::/28"];
+ public static readonly discardOnly: string[] = ["100::/64"];
+ public static readonly documentation: string[] = ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24", "2001:db8::/32", "3fff::/20"];
+ public static readonly dummy: string[] = ["192.0.0.8/32", "100:0:0:1::/64"];
+ public static readonly ietfProtocol: string[] = ["192.0.0.0/24", "2001::/23"];
+ public static readonly imds: string[] = ["169.254.169.254/32"];
+ public static readonly ipv4Ipv6Translat: string[] = ["64:ff9b::/96", "64:ff9b:1::/48"];
+ public static readonly ipv4ServiceContinuity: string[] = ["192.0.0.0/29"];
+ public static readonly broadcast: string[] = ["255.255.255.255/32"];
+ public static readonly linkLocal: string[] = ["169.254.0.0/16", "fe80::/10"];
+ public static readonly loopback: string[] = ["127.0.0.0/8", "::1/128"];
+ public static readonly multicast: string[] = ["224.0.0.0/4", "ff00::/8"];
+ public static readonly orchidv2: string[] = ["2001:20::/28"];
+ public static readonly privateUse: string[] = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"];
+ public static readonly reserved: string[] = ["240.0.0.0/4"];
+ public static readonly sharedAddressSpace: string[] = ["100.64.0.0/10"];
+ public static readonly siteLocal: string[] = ["fec0::/10"];
+ public static readonly sixto4: string[] = ["2002::/16"];
+ public static readonly srv6Sid: string[] = ["5f00::/16"];
+ public static readonly teredo: string[] = ["2001::/32"];
+ public static readonly uniqueLocal: string[] = ["fc00::/7"];
+ public static readonly unspecified: string[] = ["0.0.0.0/8", "::/128"];
+ public static readonly wireserver: string[] = ["168.63.129.16/32"];
+ public static readonly recommendedV1: string[] = ["0.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "168.63.129.16/32", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.88.99.0/24", "192.168.0.0/16", "192.175.48.0/24", "198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "224.0.0.0/4", "240.0.0.0/4", "::1/128", "::/128", "64:ff9b::/96", "64:ff9b:1::/48", "100::/64", "100:0:0:1::/64", "2001::/23", "2001:db8::/32", "2002::/16", "2620:4f:8000::/48", "3fff::/20", "5f00::/16", "fc00::/7", "fe80::/10", "fec0::/10", "ff00::/8"];
+
+ /**
+ * recommendedLatest always points to the latest version
+ */
+ public static readonly recommendedLatest: string[] = IPAddressRanges.recommendedV1;
+}
diff --git a/scripts/build-ip-ranges-cs.sh b/scripts/build-ip-ranges-cs.sh
new file mode 100755
index 0000000..932c472
--- /dev/null
+++ b/scripts/build-ip-ranges-cs.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+# Script to build IPAddressRanges.cs from IPAddressRanges.json
+# Only includes cidr ranges as C# static readonly arrays
+
+set -e
+
+# Get the directory where this script is located
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+JSON_FILE="$SCRIPT_DIR/../config/IPAddressRanges.json"
+CS_FILE="$SCRIPT_DIR/../csharp/config/IPAddressRanges.cs"
+
+# Check if jq is available
+if ! command -v jq &> /dev/null; then
+ echo "Error: jq is required but not installed. Install with: brew install jq"
+ exit 1
+fi
+
+# Check if JSON file exists
+if [[ ! -f "$JSON_FILE" ]]; then
+ echo "Error: IPAddressRanges.json not found in $SCRIPT_DIR"
+ exit 1
+fi
+
+# Create directory if it doesn't exist
+mkdir -p "$(dirname "$CS_FILE")"
+
+# Generate C# file
+cat > "$CS_FILE" << 'EOF'
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// Auto-generated from IPAddressRanges.json
+// Do not edit this file manually
+
+namespace Microsoft.Security.AntiSSRF
+{
+ ///
+ /// Static IP address ranges for AntiSSRF protection
+ ///
+ public static class IPAddressRanges
+ {
+EOF
+
+# Generate individual field declarations
+jq -r '
+ to_entries
+ | map(select(.key != "_sources" and .value.standaloneVariable == true))
+ | .[]
+ | " public static readonly string[] " + .key + " = { " + (.value.cidr | map("\"" + . + "\"") | join(", ")) + " };"
+' "$JSON_FILE" >> "$CS_FILE"
+
+# Add recommendedLatest property that matches recommendedV1
+cat >> "$CS_FILE" << 'EOF'
+
+ ///
+ /// recommendedLatest always points to the latest version
+ ///
+ public static readonly string[] recommendedLatest = recommendedV1;
+EOF
+
+
+
+# Close the class and namespace
+cat >> "$CS_FILE" << 'EOF'
+ }
+}
+EOF
+
+echo "Successfully generated $CS_FILE"
\ No newline at end of file
diff --git a/scripts/build-ip-ranges-nodejs.sh b/scripts/build-ip-ranges-nodejs.sh
new file mode 100755
index 0000000..b0b27d6
--- /dev/null
+++ b/scripts/build-ip-ranges-nodejs.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+# Script to build IPAddressRanges.ts from IPAddressRanges.json
+# Generates a TypeScript class with static readonly properties
+
+set -e
+
+# Get the directory where this script is located
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+JSON_FILE="$SCRIPT_DIR/../config/IPAddressRanges.json"
+TS_FILE="$SCRIPT_DIR/../nodejs/config/IPAddressRanges.ts"
+
+# Check if jq is available
+if ! command -v jq &> /dev/null; then
+ echo "Error: jq is required but not installed. Install with: brew install jq"
+ exit 1
+fi
+
+# Check if JSON file exists
+if [[ ! -f "$JSON_FILE" ]]; then
+ echo "Error: IPAddressRanges.json not found in $SCRIPT_DIR"
+ exit 1
+fi
+
+# Generate TypeScript file
+echo "// Copyright (c) Microsoft Corporation." > "$TS_FILE"
+echo "// Licensed under the MIT License." >> "$TS_FILE"
+echo "" >> "$TS_FILE"
+echo "// Auto-generated from IPAddressRanges.json" >> "$TS_FILE"
+echo "// Do not edit this file manually" >> "$TS_FILE"
+echo "" >> "$TS_FILE"
+echo "/**" >> "$TS_FILE"
+echo " * Static IP address ranges for AntiSSRF protection" >> "$TS_FILE"
+echo " */" >> "$TS_FILE"
+echo "export class IPAddressRanges {" >> "$TS_FILE"
+
+# Generate individual property declarations
+jq -r '
+ to_entries
+ | map(select(.key != "_sources" and .value.standaloneVariable == true))
+ | .[]
+ | " public static readonly " + .key + ": string[] = [" + (.value.cidr | map("\"" + . + "\"") | join(", ")) + "];"
+' "$JSON_FILE" >> "$TS_FILE"
+
+# Add recommendedLatest property that matches recommendedV1
+echo "" >> "$TS_FILE"
+echo " /**" >> "$TS_FILE"
+echo " * recommendedLatest always points to the latest version" >> "$TS_FILE"
+echo " */" >> "$TS_FILE"
+echo " public static readonly recommendedLatest: string[] = IPAddressRanges.recommendedV1;" >> "$TS_FILE"
+
+# Close the class
+echo "}" >> "$TS_FILE"
+
+echo "Successfully generated $TS_FILE"
\ No newline at end of file