Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/check-license-headers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
-name "*.cs" -o \
-name "*.ts" -o \
-name "*.js" -o \
-name "*.rs" -o \
-name "*.sh" -o \
-name "*.yml" -o \
-name "*.yaml" \
Expand All @@ -57,7 +58,7 @@ jobs:
missing_headers+=("$file")
fi
;;
*.cs|*.ts|*.js)
*.cs|*.ts|*.js|*.rs)
if ! head -5 "$file" | grep -q "// Copyright (c) Microsoft Corporation."; then
missing_headers+=("$file")
fi
Expand All @@ -77,7 +78,7 @@ jobs:
echo " # Copyright (c) Microsoft Corporation."
echo " # Licensed under the MIT License."
echo ""
echo "For .cs/.ts/.js files:"
echo "For .cs/.ts/.js/.rs files:"
echo " // Copyright (c) Microsoft Corporation."
echo " // Licensed under the MIT License."
exit 1
Expand Down
21 changes: 11 additions & 10 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ jobs:
build-mode: none
- language: javascript-typescript
build-mode: none
- language: rust
build-mode: manual
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
Expand Down Expand Up @@ -70,6 +72,7 @@ jobs:
- 'dotnet/FunctionalTests/**'
- 'dotnet/UnitTests/**'
- 'nodejs/tests/**'
- 'rust/tests/**'
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
Expand All @@ -83,16 +86,14 @@ jobs:
# to build your code.
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# - name: Run manual build steps
# if: matrix.build-mode == 'manual'
# shell: bash
# run: |
# echo 'If you are using a "manual" build mode for one or more of the' \
# 'languages you are analyzing, replace this with the commands to build' \
# 'your code, for example:'
# echo ' make bootstrap'
# echo ' make release'
# exit 1
- name: Setup Rust
if: matrix.language == 'rust'
uses: dtolnay/rust-toolchain@stable

- name: Build Rust for CodeQL
if: matrix.language == 'rust'
working-directory: rust
run: cargo build --verbose

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/test-rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

name: Run Rust Tests

on:
pull_request:
branches:
- main
paths:
- "rust/**"
- ".github/workflows/test-rust.yml"
workflow_dispatch:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
toolchain: [stable, beta]

steps:
- name: Checkout repository
uses: actions/checkout@v5

- name: Setup Rust ${{ matrix.toolchain }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.toolchain }}

- name: Cache cargo dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: "rust"

- name: Build
working-directory: rust
run: cargo build --verbose

- name: Run lib tests (no network features)
working-directory: rust
run: cargo test --lib --verbose

- name: Run all tests (with reqwest integration)
working-directory: rust
run: cargo test --all-features --verbose
11 changes: 8 additions & 3 deletions .github/workflows/update-domains.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
- 'config/Domains.json'
- 'nodejs/src/Helpers/Domains.ts'
- 'dotnet/src/Helpers/Domains.cs'
- 'rust/src/domains.rs'
workflow_dispatch: # Allow manual triggering

permissions:
Expand Down Expand Up @@ -38,17 +39,21 @@ jobs:
- name: Build C# version
run: ./scripts/build-domains-dotnet.sh

- name: Build Rust version
run: ./scripts/build-domains-rust.sh

- name: Verify generated files are up-to-date
run: |
if ! git diff --exit-code nodejs/src/Helpers/Domains.ts dotnet/src/Helpers/Domains.cs; then
if ! git diff --exit-code nodejs/src/Helpers/Domains.ts dotnet/src/Helpers/Domains.cs rust/src/domains.rs; then
echo "❌ Generated domain files are out of sync!"
echo "The following files need to be regenerated:"
git diff --name-only nodejs/src/Helpers/Domains.ts dotnet/src/Helpers/Domains.cs
git diff --name-only nodejs/src/Helpers/Domains.ts dotnet/src/Helpers/Domains.cs rust/src/domains.rs
echo ""
echo "Please run the following commands locally and commit the results:"
echo " ./scripts/build-domains-nodejs.sh"
echo " ./scripts/build-domains-dotnet.sh"
echo " ./scripts/build-domains-rust.sh"
exit 1
else
echo "✅ All generated files are up-to-date"
fi
fi
9 changes: 7 additions & 2 deletions .github/workflows/update-ip-ranges.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
- 'config/IPAddressRanges.json'
- 'nodejs/src/IPAddressRanges.ts'
- 'dotnet/src/IPAddressRanges.cs'
- 'rust/src/ip_address_ranges.rs'
workflow_dispatch: # Allow manual triggering

permissions:
Expand Down Expand Up @@ -38,16 +39,20 @@ jobs:
- name: Build C# version
run: ./scripts/build-ip-ranges-dotnet.sh

- name: Build Rust version
run: ./scripts/build-ip-ranges-rust.sh

- name: Verify generated files are up-to-date
run: |
if ! git diff --exit-code nodejs/src/IPAddressRanges.ts dotnet/src/IPAddressRanges.cs; then
if ! git diff --exit-code nodejs/src/IPAddressRanges.ts dotnet/src/IPAddressRanges.cs rust/src/ip_address_ranges.rs; then
echo "❌ Generated IP address range files are out of sync!"
echo "The following files need to be regenerated:"
git diff --name-only nodejs/src/IPAddressRanges.ts dotnet/src/IPAddressRanges.cs
git diff --name-only nodejs/src/IPAddressRanges.ts dotnet/src/IPAddressRanges.cs rust/src/ip_address_ranges.rs
echo ""
echo "Please run the following commands locally and commit the results:"
echo " ./scripts/build-ip-ranges-nodejs.sh"
echo " ./scripts/build-ip-ranges-dotnet.sh"
echo " ./scripts/build-ip-ranges-rust.sh"
exit 1
else
echo "✅ All generated files are up-to-date"
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Microsoft AntiSSRF

The Microsoft AntiSSRF library is a security-developed, exhaustively-tested secure code library that provides robust URL validation to mitigate the risk of Server-Side Request Forgery (SSRF) vulnerabilities. It is an easy-to-use drop-in library with minimal adoption effort for developers, available for both .NET and Node.js applications.
The Microsoft AntiSSRF library is a security-developed, exhaustively-tested secure code library that provides robust URL validation to mitigate the risk of Server-Side Request Forgery (SSRF) vulnerabilities. It is an easy-to-use drop-in library with minimal adoption effort for developers, available for .NET, Node.js, and Rust applications.

## What is Server-Side Request Forgery (SSRF)?

Expand Down Expand Up @@ -47,6 +47,13 @@ AntiSSRF helps mitigate these risks by:
- **Quick Start**: [Getting Started Guide](https://microsoft.github.io/AntiSSRF/getting-started)
- **Library README**: [Node.js README](nodejs/README.md)

### Rust

- **crates.io Package**: [antissrf](https://crates.io/crates/antissrf)
- **Documentation**: [AntiSSRF Rust API Documentation](https://microsoft.github.io/AntiSSRF/rust-api/)
- **Quick Start**: [Getting Started Guide](https://microsoft.github.io/AntiSSRF/getting-started)
- **Library README**: [Rust README](rust/README.md)

## Contributing

We welcome contributions! Please see our contribution resources:
Expand Down
37 changes: 32 additions & 5 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ Install the npm package:
npm install @microsoft/antissrf
```

### Rust

Install the crate:

```bash
cargo add antissrf
```

## Quick Start Examples

### .NET Usage
Expand Down Expand Up @@ -72,16 +80,35 @@ https.get(options, (res) => {
});
```

### Rust Usage

```rust
use antissrf::{AntiSSRFPolicy, PolicyConfigOptions};

// Create a policy for external-only requests
let policy = AntiSSRFPolicy::new(PolicyConfigOptions::ExternalOnlyLatest);

// Validate a request URL and headers
let mut headers = vec![];
let allowed = policy.validate_request("https://api.example.com/data", &mut headers)?;

// With reqwest middleware (DNS-level IP blocking)
use antissrf::network::reqwest_integration::AntiSSRFClientBuilder;

let client = AntiSSRFClientBuilder::new(policy)
.build_with_middleware()?;
```

## How to Use

The AntiSSRF library provides validation for different scenarios based on your trust requirements:

| Use Case | Description | Documentation Link |
|----------|-------------|-------------------|
| **General Case** | The untrusted URL can belong to **any domain** or an **untrusted domain**. | [.NET](dotnet-api/antissrfpolicy/) \| [Node.js](nodejs-api/antissrfpolicy/) |
| **Azure Key Vault Domain** | The untrusted URL must be an **Azure Key Vault endpoint**. | [.NET](dotnet-api/urivalidator/inazurekeyvaultdomain) \| [Node.js](nodejs-api/urivalidator/inazurekeyvaultdomain) |
| **Azure Storage Domain** | The untrusted URL must be an **Azure Storage endpoint**. | [.NET](dotnet-api/urivalidator/inazurestoragedomain) \| [Node.js](nodejs-api/urivalidator/inazurestoragedomain) |
| **Allowlist of Trusted Domains** | The untrusted URL must belong to a **specific, trusted domain**. | [.NET](dotnet-api/urivalidator/indomain) \| [Node.js](nodejs-api/urivalidator/indomain) |
| **General Case** | The untrusted URL can belong to **any domain** or an **untrusted domain**. | [.NET](dotnet-api/antissrfpolicy/) \| [Node.js](nodejs-api/antissrfpolicy/) \| [Rust](rust-api/antissrfpolicy/) |
| **Azure Key Vault Domain** | The untrusted URL must be an **Azure Key Vault endpoint**. | [.NET](dotnet-api/urivalidator/inazurekeyvaultdomain) \| [Node.js](nodejs-api/urivalidator/inazurekeyvaultdomain) \| [Rust](rust-api/urivalidator/inazurekeyvaultdomain) |
| **Azure Storage Domain** | The untrusted URL must be an **Azure Storage endpoint**. | [.NET](dotnet-api/urivalidator/inazurestoragedomain) \| [Node.js](nodejs-api/urivalidator/inazurestoragedomain) \| [Rust](rust-api/urivalidator/inazurestoragedomain) |
| **Allowlist of Trusted Domains** | The untrusted URL must belong to a **specific, trusted domain**. | [.NET](dotnet-api/urivalidator/indomain) \| [Node.js](nodejs-api/urivalidator/indomain) \| [Rust](rust-api/urivalidator/indomain) |

## Best Practices

Expand All @@ -105,7 +132,7 @@ The AntiSSRF library provides validation for different scenarios based on your t
## Next Steps

### Learn More
- 📖 **API Documentation**: [.NET API](dotnet-api/) \| [Node.js API](nodejs-api/)
- 📖 **API Documentation**: [.NET API](dotnet-api/) \| [Node.js API](nodejs-api/) \| [Rust API](rust-api/)
- ❓ **Common Questions**: [FAQ](faq)

### Get Support
Expand Down
14 changes: 6 additions & 8 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,18 @@ AntiSSRF helps mitigate these risks by:

## Supported Languages and Frameworks

| Language | Documentation | Notes |
| --- | --- | --- |
| C# | [AntiSSRF .NET Library](dotnet-api/) | For web clients using `HttpClient` objects |
| JavaScript/TypeScript | [AntiSSRF Node.js Library](nodejs-api/) | For requests using NodeJS HTTP(S) Agents |

{: .note }
> Broader platform support is under development.
| Language | Documentation | Package | Notes |
| --- | --- | --- | --- |
| C# | [AntiSSRF .NET Library](dotnet-api/) | [NuGet](https://www.nuget.org/packages/Microsoft.Security.AntiSSRF/) | For web clients using `HttpClient` objects |
| JavaScript/TypeScript | [AntiSSRF Node.js Library](nodejs-api/) | [npm](https://www.npmjs.com/package/@microsoft/antissrf) | For requests using NodeJS HTTP(S) Agents |
| Rust | [AntiSSRF Rust Library](rust-api/) | [crates.io](https://crates.io/crates/antissrf) | For reqwest clients with middleware support |

## Next Steps

### Learn More

- 🚀 **Getting Started**: [Installation and Quick Start Guide](getting-started)
- 📖 **API Documentation**: [.NET API](dotnet-api) \| [Node.js API](nodejs-api)
- 📖 **API Documentation**: [.NET API](dotnet-api) \| [Node.js API](nodejs-api) \| [Rust API](rust-api)
- ❓ **Common Questions**: [FAQ](faq)

### Get Support
Expand Down
80 changes: 80 additions & 0 deletions docs/rust-api/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
layout: default
title: Rust API Reference
nav_order: 5
description: "Complete API documentation for the AntiSSRF Rust Library"
has_children: true
has_toc: false
---

# API Documentation

## AntiSSRF Rust Library

The **AntiSSRF Rust Library** (`antissrf`) is a crate for Rust applications that provides robust URL validation and HTTP request protection to prevent SSRF vulnerabilities. It integrates with `reqwest` via `reqwest-middleware` for DNS-level IP blocking, header validation, protocol enforcement, and redirect-chain re-validation.

## Usage Instructions

The AntiSSRF library provides validation for different scenarios based on your trust requirements:

| Use Case | Description | Documentation |
| --- | --- | --- |
| **General Case** | Block internal/sensitive IP addresses, enforce headers, validate protocols. | [`AntiSSRFPolicy`](antissrfpolicy) |
| **Azure Key Vault Domain** | Validate URL belongs to an Azure Key Vault domain. | [`URIValidator::in_azure_key_vault_domain`](urivalidator/inazurekeyvaultdomain) |
| **Azure Storage Domain** | Validate URL belongs to an Azure Storage domain. | [`URIValidator::in_azure_storage_domain`](urivalidator/inazurestoragedomain) |
| **Trusted Domain Allowlist** | Validate URL belongs to a specific, trusted domain. | [`URIValidator::in_domain`](urivalidator/indomain) |

## Modules

| Module | Description |
| --- | --- |
| [`policy`](antissrfpolicy) | [`AntiSSRFPolicy`](antissrfpolicy) — central configuration object with allowlist, denylist, and header enforcement |
| [`error`](error) | [`AntiSSRFError`](error) — typed error variants with clear security semantics |
| [`cidr`](cidr) | [`CIDRBlock`](cidr) — CIDR block parsing and IP containment with IPv6 normalization |
| [`uri_validator`](urivalidator) | [`URIValidator`](urivalidator) — domain and Azure service URL validation |
| [`network`](network) | reqwest middleware integration with DNS-level IP blocking and redirect re-validation |

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
antissrf = "0.1.1"
```

Or with explicit feature control:

```toml
# Core only (no HTTP client dependencies)
antissrf = { version = "0.1.1", default-features = false }

# Full reqwest integration (default)
antissrf = { version = "0.1.1", features = ["reqwest-integration"] }
```

## Quick Start

```rust
use antissrf::{AntiSSRFPolicy, PolicyConfigOptions};

// Block all known dangerous IPs
let policy = AntiSSRFPolicy::new(PolicyConfigOptions::ExternalOnlyLatest);

// Validate a request
let mut headers = vec![];
let allowed = policy.validate_request("https://api.example.com", &mut headers)?;
```

## Feature Flags

| Flag | Default | Description |
| --- | --- | --- |
| `reqwest-integration` | Yes | Enables `network` module with reqwest / reqwest-middleware support |

## References

- [crates.io](https://crates.io/crates/antissrf)
- [docs.rs](https://docs.rs/antissrf)
- [Standalone Repository](https://github.com/finn79426/AntiSSRF-rs)
- [Microsoft AntiSSRF Documentation](https://microsoft.github.io/AntiSSRF/)
5 changes: 5 additions & 0 deletions rust/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Rust build output
target/

# Local development artifacts
*.local.*
Loading