Skip to content
Draft
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
93 changes: 90 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
10.0.x
dotnet-version: 10.0.x
- run: dotnet test --verbosity normal
working-directory: c#/BunnyCDN.TokenAuthentication
- name: Emit tokens
run: |
dotnet build Emit -v q --nologo
dotnet run --project Emit --no-build -v q > "$GITHUB_WORKSPACE/tokens-csharp.json"
working-directory: c#/BunnyCDN.TokenAuthentication
- uses: actions/upload-artifact@v4
with:
name: tokens-csharp
path: tokens-csharp.json

python:
name: Python ${{ matrix.python-version }}
Expand All @@ -45,6 +52,12 @@ jobs:
node-version: '20'
- run: node --test token.test.js
working-directory: nodejs
- name: Emit tokens
run: node e2e/emit.js > tokens-nodejs.json
- uses: actions/upload-artifact@v4
with:
name: tokens-nodejs
path: tokens-nodejs.json

php:
name: PHP
Expand All @@ -59,6 +72,12 @@ jobs:
working-directory: php
- run: vendor/bin/phpunit
working-directory: php
- name: Emit tokens
run: php e2e/emit.php > tokens-php.json
- uses: actions/upload-artifact@v4
with:
name: tokens-php
path: tokens-php.json

java:
name: Java (Maven)
Expand All @@ -75,6 +94,15 @@ jobs:
maven-version: '3.9.9'
- run: mvn test --batch-mode --no-transfer-progress
working-directory: java
- name: Emit tokens
run: |
javac -d out src/main/java/BunnyCDN/TokenSigner.java src/main/java/BunnyCDN/Emit.java
java -cp out BunnyCDN.Emit "$GITHUB_WORKSPACE/e2e/inputs.json" > "$GITHUB_WORKSPACE/tokens-java.json"
working-directory: java
- uses: actions/upload-artifact@v4
with:
name: tokens-java
path: tokens-java.json

go:
name: Go
Expand All @@ -86,6 +114,13 @@ jobs:
go-version: '1.21'
- run: go test -v
working-directory: go
- name: Emit tokens
run: go run ./cmd/emit "$GITHUB_WORKSPACE/e2e/inputs.json" > "$GITHUB_WORKSPACE/tokens-go.json"
working-directory: go
- uses: actions/upload-artifact@v4
with:
name: tokens-go
path: tokens-go.json

rust:
name: Rust
Expand All @@ -95,3 +130,55 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
- run: cargo test --verbose
working-directory: rust
- name: Emit tokens
run: cargo run --example emit > "$GITHUB_WORKSPACE/tokens-rust.json"
working-directory: rust
- uses: actions/upload-artifact@v4
with:
name: tokens-rust
path: tokens-rust.json

vectors:
name: e2e vectors regen + cross-language consistency
needs: [csharp, python, nodejs, php, java, go, rust]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Regenerate vectors.json
run: python e2e/gen_vectors.py

- name: Auto-commit regenerated vectors.json
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
run: |
if git diff --quiet -- e2e/vectors.json; then
echo "vectors.json already up to date."
else
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add e2e/vectors.json
git commit -m "chore(e2e): regenerate vectors.json [skip ci]"
git push
echo "::notice::vectors.json was stale — regenerated and committed to the PR branch."
fi

- name: Fail if stale (cannot auto-commit here)
if: "!(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)"
run: |
if ! git diff --quiet -- e2e/vectors.json; then
echo "::error::e2e/vectors.json is stale. Run 'python e2e/gen_vectors.py' and commit."
exit 1
fi
echo "vectors.json is up to date."

- uses: actions/download-artifact@v4
with:
path: emitted
- run: python e2e/compare_vectors.py emitted/*/*.json
36 changes: 36 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Build artifacts and caches across all signer languages.
# Source lives in e2e/, go/cmd/, rust/examples/, */src — those are tracked;
# only generated output is ignored here.

# Rust
/rust/target/
/rust/Cargo.lock

# .NET
/c#/**/bin/
/c#/**/obj/

# Java
/java/out/
/java/target/
*.class

# Node
**/node_modules/

# PHP
/php/vendor/

# Python
__pycache__/
*.py[cod]
.pytest_cache/

# Editors / IDEs
.idea/
.vs/

# e2e cross-language emit output (generated token dumps + downloaded artifacts;
# the committed e2e sources are inputs.json / vectors.json / *.py / emit.*)
/tokens-*.json
/emitted/
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net10.0;net6.0</TargetFrameworks>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="Shouldly" Version="4.0.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="Shouldly" Version="4.2.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void WithIPAddress()
t.UserIp = "1.2.3.4";
});

url.ShouldBe("https://token-tester.b-cdn.net/300kb.jpg?token=HS256-0A9FRzMI9ACT-5VKMPbJf7g8f7UHavqjBH1Z8HljoEk&expires=1598024587");
url.ShouldBe("https://token-tester.b-cdn.net/300kb.jpg?token=HS256-1-L2rISTLcujMY9UFf2tbZ41d5i-Bme1g1oTK_Z2QMLJk&expires=1598024587");
}

[Test]
Expand All @@ -71,7 +71,28 @@ public void WithIPv6Address()
t.UserIp = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
});

url.ShouldBe("https://token-tester.b-cdn.net/300kb.jpg?token=HS256-7CEOZ-eY9DjC36ZnazCM3Ykj3-bR6h9V_IncIVT2s2U&expires=1598024587");
url.ShouldBe("https://token-tester.b-cdn.net/300kb.jpg?token=HS256-1-Z1BaGdhTZdU4iANcyKpurFR2VgCNdqC6hlBv5x_TyaI&expires=1598024587");
}

[Test]
public void IPv6CompressedFormMatchesExpanded()
{
var expanded = TokenSigner.SignUrl(t =>
{
t.Url = "https://token-tester.b-cdn.net/300kb.jpg";
t.SecurityKey = SecurityKey;
t.ExpiresAt = ExpiresAtGlobal;
t.UserIp = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
});
var compressed = TokenSigner.SignUrl(t =>
{
t.Url = "https://token-tester.b-cdn.net/300kb.jpg";
t.SecurityKey = SecurityKey;
t.ExpiresAt = ExpiresAtGlobal;
t.UserIp = "2001:db8:85a3::8a2e:370:7334";
});

compressed.ShouldBe(expanded);
}

[Test]
Expand All @@ -87,7 +108,7 @@ public void CombinedIPv6CountryDirectory()
t.CountriesAllowed = new List<string> { "CA", "US" };
});

url.ShouldBe("https://token-tester.b-cdn.net/bcdn_token=HS256-om4aK_1Gnb3m2_5WVMtLzD-vlubUyDo1mJ0FFrKU1Kk&token_countries=CA%2CUS&expires=1598024587/abc/");
url.ShouldBe("https://token-tester.b-cdn.net/bcdn_token=HS256-1-LGpoP8i1bV4P1kN4NrO_iYxLJuLAD2R3Clstsy9kWAc&token_countries=CA%2CUS&expires=1598024587/abc/");
}

[Test]
Expand Down Expand Up @@ -191,7 +212,7 @@ public void CombinedIPCountryDirectory()
t.CountriesAllowed = new List<string> { "CA", "US" };
});

url.ShouldBe("https://token-tester.b-cdn.net/bcdn_token=HS256-pj8ytucbBWXT_M5cAqKGu4pshB2Q_s28G2uMfjhc3lA&token_countries=CA%2CUS&expires=1598024587/abc/");
url.ShouldBe("https://token-tester.b-cdn.net/bcdn_token=HS256-1-4lIDGI2_t3wiTmopXzB7z71wtZKTe1Ic0lDlL72iAJw&token_countries=CA%2CUS&expires=1598024587/abc/");
}

[Test]
Expand Down Expand Up @@ -239,7 +260,7 @@ public void CombinedSpeedLimitIPDirectory()
t.SpeedLimit = 5000;
});

url.ShouldBe("https://token-tester.b-cdn.net/bcdn_token=HS256-9M87MQhNKZqVdjqgHo1IMFVNa01tL2DwlmjBCtou08I&limit=5000&expires=1598024587/abc/");
url.ShouldBe("https://token-tester.b-cdn.net/bcdn_token=HS256-1-X01Z6A9xAo1_ds1XFf9y8gAIzk_JpmoevOx7EtgMQhY&limit=5000&expires=1598024587/abc/");
}

[Test]
Expand All @@ -264,5 +285,31 @@ public void ValidationMissingExpiry()
t.SecurityKey = SecurityKey;
}));
}

[Test]
public void NoUserIpOmitsFlagPrefix()
{
var url = TokenSigner.SignUrl(t =>
{
t.Url = "https://token-tester.b-cdn.net/300kb.jpg";
t.SecurityKey = SecurityKey;
t.ExpiresAt = ExpiresAtGlobal;
});

url.ShouldNotContain("HS256-1-");
}

[Test]
public void InvalidIpThrows()
{
Should.Throw<ArgumentException>(() =>
TokenSigner.SignUrl(t =>
{
t.Url = "https://token-tester.b-cdn.net/300kb.jpg";
t.SecurityKey = SecurityKey;
t.ExpiresAt = ExpiresAtGlobal;
t.UserIp = "not-an-ip";
}));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<TargetFrameworks>net10.0;netstandard2.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Version>2.1.0</Version>
</PropertyGroup>

</Project>
Loading