diff --git a/.editorconfig b/.editorconfig
index 4c88e6b..3e3a11b 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -53,7 +53,7 @@ dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
-end_of_line = crlf
+end_of_line = lf
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 0000000..fd64c20
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,35 @@
+# HttpSecurity.AspNet — AI Development Guide
+
+> Open-source ASP.NET class library for HTTP security headers and Content Security Policy
+
+---
+
+## Quick Reference
+
+| Item | Value |
+| :--------------- | :------------------------------------------------- |
+| **Framework** | .NET 8.0 (`net8.0`) — runs on .NET 8, 9, 10 |
+| **Build** | `dotnet build HttpSecurity.AspNet.slnx` |
+| **Pack** | `dotnet pack HttpSecurity.AspNet/HttpSecurity.AspNet.csproj` |
+| **Source gen** | `SourceGenerator/` targets `netstandard2.0` |
+
+---
+
+## Project Architecture
+
+| Project | Purpose | TFM |
+| :----------------------- | :------------------------------------------------- | :---------------- |
+| **HttpSecurity.AspNet** | Library: CSP, headers, SRI hash generation | `net8.0` |
+| **SourceGenerator** | Roslyn source generator for hash computation | `netstandard2.0` |
+| **HttpSecurity.Example** | Example Blazor Server app | `net8.0` |
+
+---
+
+## C# Standards
+
+- `ImplicitUsings=enable`, `Nullable=enable`
+- Interfaces: `I` prefix
+- Types, properties, methods: PascalCase
+- File-scoped namespaces
+- Expression-bodied members where appropriate
+- Null-coalescing (`??`) and null-propagation (`?.`) preferred
diff --git a/.github/workflows/GithubActionsRelease.yml b/.github/workflows/GithubActionsRelease.yml
index 5aaeed7..06b8ad6 100644
--- a/.github/workflows/GithubActionsRelease.yml
+++ b/.github/workflows/GithubActionsRelease.yml
@@ -11,38 +11,37 @@
on:
push:
tags:
- - '*' # Push events to matching *, i.e. 1.0, 20.15.10
+ - "*" # Push events to matching *, i.e. 1.0, 20.15.10
env:
- buildPlatform: 'Any CPU'
- buildConfiguration: 'Release'
+ buildPlatform: "Any CPU"
+ buildConfiguration: "Release"
outputCSFB: ${{github.workspace}}\siteCSFB
- projectCSFB: 'HttpSecurity.AspNet/HttpSecurity.AspNet.csproj'
-
-jobs:
+ projectCSFB: "HttpSecurity.AspNet/HttpSecurity.AspNet.csproj"
-############################################################################################################
-# These jobs are used to gate actions. By creating these jobs we don't need to proliferate the repo checks
-############################################################################################################
+jobs:
+ ############################################################################################################
+ # These jobs are used to gate actions. By creating these jobs we don't need to proliferate the repo checks
+ ############################################################################################################
is-on-fork:
name: Running on a fork?
runs-on: ubuntu-latest
if: github.repository != 'Material-Blazor/HttpSecurity.AspNet'
steps:
- - name: Nothing to see here
- run: echo ""
+ - name: Nothing to see here
+ run: echo ""
is-on-material-blazor:
name: Running on Material-Blazor/HttpSecurity.AspNet?
runs-on: ubuntu-latest
if: github.repository == 'Material-Blazor/HttpSecurity.AspNet'
steps:
- - name: Nothing to see here
- run: echo ""
+ - name: Nothing to see here
+ run: echo ""
-############################################################################################################
-# Build package and deploy
-############################################################################################################
+ ############################################################################################################
+ # Build package and deploy
+ ############################################################################################################
build-and-deploy-package:
name: Build nuget package & deploy to nuget
needs: [is-on-material-blazor]
@@ -50,36 +49,35 @@ jobs:
runs-on: windows-latest
steps:
- - name: Get the version
- run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
- shell: bash
-
- - name: Checkout repository under $GITHUB_WORKSPACE so the job can access it 🛎️
- uses: actions/checkout@v4
- with:
- persist-credentials: false
+ - name: Get the version
+ run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
+ shell: bash
- - name: Use dotnet
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: '8.x'
+ - name: Checkout repository under $GITHUB_WORKSPACE so the job can access it 🛎️
+ uses: actions/checkout@v6
+ with:
+ persist-credentials: false
- - name: Build HttpSecurity.AspNet 🔧
- run: dotnet build ${{env.projectCSFB}} --configuration ${{env.buildConfiguration}} -p:Version=${{env.version}}
+ - name: Use dotnet
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: "10.x"
- - name: Generate the NuGet package 🔧
- run: dotnet pack ${{env.projectCSFB}} --no-build --configuration ${{env.buildConfiguration}} --output ${{env.outputCSFB}} -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:Version=${{env.version}}
+ - name: Build HttpSecurity.AspNet 🔧
+ run: dotnet build ${{env.projectCSFB}} --configuration ${{env.buildConfiguration}} -p:Version=${{env.version}}
- - name: Display HttpSecurity.AspNet package output Ꙫ
- run: dir ${{env.outputCSFB}}
+ - name: Generate the NuGet package 🔧
+ run: dotnet pack ${{env.projectCSFB}} --no-build --configuration ${{env.buildConfiguration}} --output ${{env.outputCSFB}} -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:Version=${{env.version}}
- - name: Upload Package 🚀
- run: dotnet nuget push ${{env.outputCSFB}}\*.nupkg -k ${{secrets.NUGET_API_KEY}} -s https://api.nuget.org/v3/index.json
+ - name: Display HttpSecurity.AspNet package output Ꙫ
+ run: dir ${{env.outputCSFB}}
+ - name: Upload Package 🚀
+ run: dotnet nuget push ${{env.outputCSFB}}\*.nupkg -k ${{secrets.NUGET_API_KEY}} -s https://api.nuget.org/v3/index.json
-############################################################################################################
-# Create release
-############################################################################################################
+ ############################################################################################################
+ # Create release
+ ############################################################################################################
create-release:
name: Create release
needs: [build-and-deploy-package, is-on-material-blazor]
@@ -87,12 +85,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- - name: Get the version
- run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
- shell: bash
-
- - name: Create Release
- uses: ncipollo/release-action@v1
- with:
- name: Release ${{env.version}}
- tag: ${{env.version}}
+ - name: Get the version
+ run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
+ shell: bash
+
+ - name: Create Release
+ uses: ncipollo/release-action@v1.21.0
+ with:
+ name: Release ${{env.version}}
+ tag: ${{env.version}}
diff --git a/.github/workflows/GithubActionsWIP.yml b/.github/workflows/GithubActionsWIP.yml
index b87097a..f9b0bf5 100644
--- a/.github/workflows/GithubActionsWIP.yml
+++ b/.github/workflows/GithubActionsWIP.yml
@@ -3,67 +3,66 @@
on:
push:
branches:
- - 'main'
+ - "main"
pull_request:
branches:
- - 'main'
+ - "main"
env:
- buildPlatform: 'Any CPU'
- buildConfiguration: 'Release'
+ buildPlatform: "Any CPU"
+ buildConfiguration: "Release"
outputCSFB: ${{github.workspace}}/siteCSFB
- projectCSFB: 'HttpSecurity.AspNet/HttpSecurity.AspNet.csproj'
+ projectCSFB: "HttpSecurity.AspNet/HttpSecurity.AspNet.csproj"
jobs:
-
-############################################################################################################
-# These jobs are used to gate actions. By creating these jobs we don't need to proliferate the repo checks
-############################################################################################################
+ ############################################################################################################
+ # These jobs are used to gate actions. By creating these jobs we don't need to proliferate the repo checks
+ ############################################################################################################
is-on-fork:
name: Running on a fork?
runs-on: ubuntu-latest
if: github.repository != 'Material-Blazor/HttpSecurity.AspNet'
steps:
- - name: Nothing to see here
- run: echo ""
+ - name: Nothing to see here
+ run: echo ""
is-on-material-blazor:
name: Running on Material-Blazor/HttpSecurity.AspNet?
runs-on: ubuntu-latest
if: github.repository == 'Material-Blazor/HttpSecurity.AspNet'
steps:
- - name: Nothing to see here
- run: echo ""
+ - name: Nothing to see here
+ run: echo ""
-############################################################################################################
-# Build nuget package
-############################################################################################################
+ ############################################################################################################
+ # Build nuget package
+ ############################################################################################################
build-package:
name: Build nuget package
runs-on: windows-latest
steps:
- - name: Set ciSuffix as env variable
- run: echo "ciSuffix=ci.$(date +'%Y-%m-%d--%H%M')" >> $GITHUB_ENV
- shell: bash
+ - name: Set ciSuffix as env variable
+ run: echo "ciSuffix=ci.$(date +'%Y-%m-%d--%H%M')" >> $GITHUB_ENV
+ shell: bash
- - name: Checkout repository under $GITHUB_WORKSPACE so the job can access it 🛎️
- uses: actions/checkout@v4
- with:
- persist-credentials: false
+ - name: Checkout repository under $GITHUB_WORKSPACE so the job can access it 🛎️
+ uses: actions/checkout@v6
+ with:
+ persist-credentials: false
- - name: Use dotnet
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: '8.x'
+ - name: Use dotnet
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: "10.x"
- - name: Build HttpSecurity.AspNet 🔧
- run: dotnet build ${{env.projectCSFB}} --configuration ${{env.buildConfiguration}} --version-suffix ${{env.ciSuffix}}
+ - name: Build HttpSecurity.AspNet 🔧
+ run: dotnet build ${{env.projectCSFB}} --configuration ${{env.buildConfiguration}} --version-suffix ${{env.ciSuffix}}
- - name: Generate the NuGet package 🔧
- run: dotnet pack ${{env.projectCSFB}} --no-build --configuration ${{env.buildConfiguration}} --output ${{env.outputCSFB}} -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:Version=1.0.0-${{env.ciSuffix}}
+ - name: Generate the NuGet package 🔧
+ run: dotnet pack ${{env.projectCSFB}} --no-build --configuration ${{env.buildConfiguration}} --output ${{env.outputCSFB}} -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:Version=1.0.0-${{env.ciSuffix}}
- - name: Display HttpSecurity.AspNet package output Ꙫ
- run: dir ${{env.outputCSFB}}
+ - name: Display HttpSecurity.AspNet package output Ꙫ
+ run: dir ${{env.outputCSFB}}
diff --git a/.markdownlint.json b/.markdownlint.json
new file mode 100644
index 0000000..ba25a61
--- /dev/null
+++ b/.markdownlint.json
@@ -0,0 +1,10 @@
+{
+ "MD013": {
+ "line_length": 180,
+ "code_blocks": false,
+ "tables": false
+ },
+ "MD024": false,
+ "MD033": false,
+ "MD041": false
+}
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..aa44f8b
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,18 @@
+{
+ "endOfLine": "lf",
+ "tabWidth": 2,
+ "useTabs": false,
+ "singleQuote": false,
+ "proseWrap": "preserve",
+ "printWidth": 240,
+ "overrides": [
+ {
+ "files": "*.md",
+ "options": {
+ "parser": "markdown",
+ "printWidth": 180,
+ "proseWrap": "always"
+ }
+ }
+ ]
+}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..c995e53
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,12 @@
+{
+ "recommendations": [
+ "EditorConfig.EditorConfig",
+ "esbenp.prettier-vscode",
+ "DavidAnson.vscode-markdownlint",
+ "bierner.markdown-mermaid",
+ "GitHub.copilot-chat",
+ "ms-dotnettools.csdevkit",
+ "ms-dotnettools.csharp",
+ "ms-dotnettools.vscode-dotnet-runtime"
+ ]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..16c899a
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,26 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "HttpSecurity.Example",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build HttpSecurity.Example",
+ "program": "${workspaceFolder}/.artifacts/bin/HttpSecurity.Example/debug/HttpSecurity.Example.dll",
+ "cwd": "${workspaceFolder}/HttpSecurity.Example",
+ "stopAtEntry": false,
+ "justMyCode": true,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "https://localhost:50083"
+ },
+ "serverReadyAction": {
+ "action": "openExternally",
+ "pattern": "\\bNow listening on:\\s+(https?://\\S+)"
+ }
+ }
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..c4ca9fd
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,30 @@
+{
+ "extensions.experimental.affinity": {
+ "GitHub.copilot": 1,
+ "GitHub.copilot-chat": 1
+ },
+ "github.copilot.editor.enableAutoCompletions": true,
+ "files.exclude": {
+ "**/node_modules": true,
+ "**/bin": true,
+ "**/obj": true,
+ "**/.git": true,
+ "**/.artifacts": true
+ },
+ "search.exclude": {
+ "**/node_modules": true,
+ "**/bin": true,
+ "**/obj": true,
+ "**/.artifacts": true
+ },
+ "editor.formatOnSave": true,
+ "[markdown]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true,
+ "editor.wordWrap": "wordWrapColumn",
+ "editor.wordWrapColumn": 180
+ },
+ "files.associations": {
+ "appsettings*.json": "jsonc"
+ }
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..e1fe801
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,18 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build HttpSecurity.Example",
+ "type": "shell",
+ "command": "dotnet",
+ "args": ["build", "--project", "${workspaceFolder}/HttpSecurity.Example/HttpSecurity.Example.csproj"],
+ "presentation": {
+ "reveal": "silent",
+ "panel": "dedicated",
+ "clear": false
+ },
+ "problemMatcher": "$msCompile",
+ "group": "build"
+ }
+ ]
+}
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..c04706d
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,14 @@
+# HttpSecurity.AspNet — Claude Code Instructions
+
+> Open-source ASP.NET class library for HTTP security headers and Content Security Policy
+
+## Development Guide
+
+Read and follow all rules in `.github/copilot-instructions.md` — it is the single source of truth for project conventions, coding standards, and architecture.
+
+## Available Commands
+
+| Command | Purpose |
+| :--------------------------------------------------------------- | :----------------- |
+| `dotnet build HttpSecurity.AspNet.slnx` | Build solution |
+| `dotnet pack HttpSecurity.AspNet/HttpSecurity.AspNet.csproj` | Create NuGet package |
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..9b95ed0
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,5 @@
+
+
+ $(MSBuildThisFileDirectory).artifacts
+
+
diff --git a/HttpSecurity.AspNet.sln b/HttpSecurity.AspNet.sln
deleted file mode 100644
index 7241b28..0000000
--- a/HttpSecurity.AspNet.sln
+++ /dev/null
@@ -1,48 +0,0 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.4.32912.340
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGenerator", "SourceGenerator\SourceGenerator.csproj", "{47D7D5B5-BBBD-4CD5-9FC3-4405EA55FD08}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpSecurity.Example", "HttpSecurity.Example\HttpSecurity.Example.csproj", "{7FA94ABA-D9D6-4501-A002-72CA6D1C3BC3}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A60737BC-8E5F-4B2E-BCFC-793BD95DFC14}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- .gitattributes = .gitattributes
- .gitignore = .gitignore
- .github\dependabot.yml = .github\dependabot.yml
- .github\workflows\GithubActionsRelease.yml = .github\workflows\GithubActionsRelease.yml
- .github\workflows\GithubActionsWIP.yml = .github\workflows\GithubActionsWIP.yml
- README.md = README.md
- ReleaseNotes.md = ReleaseNotes.md
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpSecurity.AspNet", "HttpSecurity.AspNet\HttpSecurity.AspNet.csproj", "{DD306EF6-02EC-4DE2-A0AA-C5325E596F00}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {47D7D5B5-BBBD-4CD5-9FC3-4405EA55FD08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {47D7D5B5-BBBD-4CD5-9FC3-4405EA55FD08}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {47D7D5B5-BBBD-4CD5-9FC3-4405EA55FD08}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {47D7D5B5-BBBD-4CD5-9FC3-4405EA55FD08}.Release|Any CPU.Build.0 = Release|Any CPU
- {7FA94ABA-D9D6-4501-A002-72CA6D1C3BC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7FA94ABA-D9D6-4501-A002-72CA6D1C3BC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7FA94ABA-D9D6-4501-A002-72CA6D1C3BC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7FA94ABA-D9D6-4501-A002-72CA6D1C3BC3}.Release|Any CPU.Build.0 = Release|Any CPU
- {DD306EF6-02EC-4DE2-A0AA-C5325E596F00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DD306EF6-02EC-4DE2-A0AA-C5325E596F00}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DD306EF6-02EC-4DE2-A0AA-C5325E596F00}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DD306EF6-02EC-4DE2-A0AA-C5325E596F00}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {DF4D493C-EC0A-489A-B4C3-DDCF33178524}
- EndGlobalSection
-EndGlobal
diff --git a/HttpSecurity.AspNet.slnx b/HttpSecurity.AspNet.slnx
new file mode 100644
index 0000000..1d1dc05
--- /dev/null
+++ b/HttpSecurity.AspNet.slnx
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/HttpSecurity.AspNet/ContentSecurityPolicies/BlockAllMixedContent.cs b/HttpSecurity.AspNet/ContentSecurityPolicies/BlockAllMixedContent.cs
index 1419ece..4606a4b 100644
--- a/HttpSecurity.AspNet/ContentSecurityPolicies/BlockAllMixedContent.cs
+++ b/HttpSecurity.AspNet/ContentSecurityPolicies/BlockAllMixedContent.cs
@@ -2,8 +2,9 @@
///
-/// block-all-mixed-content policy - considered deprecated.
+/// block-all-mixed-content policy - deprecated. Use upgrade-insecure-requests instead.
///
+[Obsolete("block-all-mixed-content is deprecated by the CSP specification. Use upgrade-insecure-requests instead.")]
[ContentSecurityPolicyOptions]
public sealed partial class BlockAllMixedContentOptions : ContentSecurityPolicyOptionsBase
{
@@ -11,8 +12,9 @@ public sealed partial class BlockAllMixedContentOptions : ContentSecurityPolicyO
///
-/// block-all-mixed-content policy - considered deprecated.
+/// block-all-mixed-content policy - deprecated. Use upgrade-insecure-requests instead.
///
+[Obsolete("block-all-mixed-content is deprecated by the CSP specification. Use upgrade-insecure-requests instead.")]
[ContentSecurityPolicy("block-all-mixed-content")]
public sealed partial class BlockAllMixedContent : ContentSecurityPolicyBase
{
diff --git a/HttpSecurity.AspNet/ContentSecurityPolicies/NavigateTo.cs b/HttpSecurity.AspNet/ContentSecurityPolicies/NavigateTo.cs
index 3e9f634..fe1847a 100644
--- a/HttpSecurity.AspNet/ContentSecurityPolicies/NavigateTo.cs
+++ b/HttpSecurity.AspNet/ContentSecurityPolicies/NavigateTo.cs
@@ -2,8 +2,9 @@
///
-/// navigate-to policy.
+/// navigate-to policy - removed from the CSP Level 3 specification with no browser support.
///
+[Obsolete("navigate-to was removed from the CSP Level 3 specification and has no browser support. Remove this directive.")]
[ContentSecurityPolicyOptions]
[AddHashValue]
[AddHostSource]
@@ -23,8 +24,9 @@ public sealed partial class NavigateToOptions : ContentSecurityPolicyOptionsBase
///
-/// navigate-to policy.
+/// navigate-to policy - removed from the CSP Level 3 specification with no browser support.
///
+[Obsolete("navigate-to was removed from the CSP Level 3 specification and has no browser support. Remove this directive.")]
[ContentSecurityPolicy("navigate-to")]
public sealed partial class NavigateTo : ContentSecurityPolicyBase
{
diff --git a/HttpSecurity.AspNet/Enumerations/CrossOriginEmbedderPolicyDirective.cs b/HttpSecurity.AspNet/Enumerations/CrossOriginEmbedderPolicyDirective.cs
new file mode 100644
index 0000000..a67a31f
--- /dev/null
+++ b/HttpSecurity.AspNet/Enumerations/CrossOriginEmbedderPolicyDirective.cs
@@ -0,0 +1,22 @@
+namespace HttpSecurity.AspNet;
+
+///
+/// Directives for the Cross-Origin-Embedder-Policy header.
+///
+public enum CrossOriginEmbedderPolicyDirective
+{
+ ///
+ /// unsafe-none — allows the document to fetch cross-origin resources without giving explicit permission (default).
+ ///
+ UnsafeNone,
+
+ ///
+ /// require-corp — prevents loading of cross-origin resources that don't explicitly grant permission via CORS or CORP.
+ ///
+ RequireCorp,
+
+ ///
+ /// credentialless — allows cross-origin no-cors requests to be sent without credentials.
+ ///
+ Credentialless,
+}
diff --git a/HttpSecurity.AspNet/Enumerations/CrossOriginOpenerPolicyDirective.cs b/HttpSecurity.AspNet/Enumerations/CrossOriginOpenerPolicyDirective.cs
new file mode 100644
index 0000000..e9498e2
--- /dev/null
+++ b/HttpSecurity.AspNet/Enumerations/CrossOriginOpenerPolicyDirective.cs
@@ -0,0 +1,22 @@
+namespace HttpSecurity.AspNet;
+
+///
+/// Directives for the Cross-Origin-Opener-Policy header.
+///
+public enum CrossOriginOpenerPolicyDirective
+{
+ ///
+ /// unsafe-none — allows the document to be added to its opener's browsing context group (default).
+ ///
+ UnsafeNone,
+
+ ///
+ /// same-origin-allow-popups — retains references to newly opened windows or tabs that either don't set COOP or opt out.
+ ///
+ SameOriginAllowPopups,
+
+ ///
+ /// same-origin — isolates the browsing context group to same-origin documents only.
+ ///
+ SameOrigin,
+}
diff --git a/HttpSecurity.AspNet/Enumerations/CrossOriginResourcePolicyDirective.cs b/HttpSecurity.AspNet/Enumerations/CrossOriginResourcePolicyDirective.cs
new file mode 100644
index 0000000..e3f48ba
--- /dev/null
+++ b/HttpSecurity.AspNet/Enumerations/CrossOriginResourcePolicyDirective.cs
@@ -0,0 +1,22 @@
+namespace HttpSecurity.AspNet;
+
+///
+/// Directives for the Cross-Origin-Resource-Policy header.
+///
+public enum CrossOriginResourcePolicyDirective
+{
+ ///
+ /// same-site — only requests from the same site can read the resource.
+ ///
+ SameSite,
+
+ ///
+ /// same-origin — only requests from the same origin can read the resource.
+ ///
+ SameOrigin,
+
+ ///
+ /// cross-origin — requests from any origin can read the resource.
+ ///
+ CrossOrigin,
+}
diff --git a/HttpSecurity.AspNet/Enumerations/SchemeSource.cs b/HttpSecurity.AspNet/Enumerations/SchemeSource.cs
index 3a202eb..0e7fa80 100644
--- a/HttpSecurity.AspNet/Enumerations/SchemeSource.cs
+++ b/HttpSecurity.AspNet/Enumerations/SchemeSource.cs
@@ -39,4 +39,16 @@ public enum SchemeSource
/// mediastream: source.
///
Mediastream,
+
+
+ ///
+ /// ws: source (unencrypted WebSocket).
+ ///
+ Ws,
+
+
+ ///
+ /// wss: source (encrypted WebSocket).
+ ///
+ Wss,
}
diff --git a/HttpSecurity.AspNet/HttpSecurity.AspNet.csproj b/HttpSecurity.AspNet/HttpSecurity.AspNet.csproj
index c627e7c..9a50a16 100644
--- a/HttpSecurity.AspNet/HttpSecurity.AspNet.csproj
+++ b/HttpSecurity.AspNet/HttpSecurity.AspNet.csproj
@@ -2,7 +2,6 @@
net8.0
- 11
enable
enable
README.md
@@ -10,8 +9,8 @@
-
-
+
+
diff --git a/HttpSecurity.AspNet/Services/HttpSecurityOptions.cs b/HttpSecurity.AspNet/Services/HttpSecurityOptions.cs
index 09b88de..526bf3c 100644
--- a/HttpSecurity.AspNet/Services/HttpSecurityOptions.cs
+++ b/HttpSecurity.AspNet/Services/HttpSecurityOptions.cs
@@ -50,11 +50,11 @@ internal string GetContentSecurityPolicy(IHttpSecurityService httpSecurityServic
public HttpSecurityOptions AddContentSecurityOptions(Action configureOptions)
{
ContentSecurityPolicyOptions options = new();
-
+
configureOptions(options);
HeaderBuilders.Add(new(
- "Content-Security-Policy",
+ "Content-Security-Policy",
(IHttpSecurityService httpSecurityService, string nonceValue, string baseUri, string baseDomain) => string.Join(' ', options.Policies.Select(x => x.GetPolicyValue(httpSecurityService, nonceValue, baseUri, baseDomain)).OrderBy(x => x))));
return this;
@@ -121,12 +121,108 @@ public HttpSecurityOptions AddPermissionsPolicy(string permissionsPolicy)
///
- /// Adds an Strict-Transport-Security directive with the value supplied.
+ /// Adds a Strict-Transport-Security header.
+ ///
+ /// The max-age value in seconds.
+ /// Whether to include the includeSubDomains directive.
+ /// Whether to include the preload directive. Only set this if you intend to submit the domain to the HSTS preload list at https://hstspreload.org.
+ ///
+ public HttpSecurityOptions AddStrictTransportSecurity(ulong maxAgeExpireTime, bool includeSubDomains = false, bool preload = false)
+ {
+ HeaderBuilders.Add(new("Strict-Transport-Security", (_, _, _, _) =>
+ $"max-age={maxAgeExpireTime}{(includeSubDomains ? "; includeSubDomains" : "")}{(preload ? "; preload" : "")}"));
+ return this;
+ }
+
+
+ ///
+ /// Adds a Content-Security-Policy-Report-Only header. Use this to test a new policy without enforcing it.
+ ///
+ /// Configures the content security policy.
+ ///
+ public HttpSecurityOptions AddContentSecurityPolicyReportOnly(Action configureOptions)
+ {
+ ContentSecurityPolicyOptions options = new();
+
+ configureOptions(options);
+
+ HeaderBuilders.Add(new(
+ "Content-Security-Policy-Report-Only",
+ (IHttpSecurityService httpSecurityService, string nonceValue, string baseUri, string baseDomain) => string.Join(' ', options.Policies.Select(x => x.GetPolicyValue(httpSecurityService, nonceValue, baseUri, baseDomain)).OrderBy(x => x))));
+
+ return this;
+ }
+
+
+ ///
+ /// Adds a Cross-Origin-Embedder-Policy header.
+ ///
+ /// The COEP directive value.
+ ///
+ public HttpSecurityOptions AddCrossOriginEmbedderPolicy(CrossOriginEmbedderPolicyDirective directive)
+ {
+ var value = directive switch
+ {
+ CrossOriginEmbedderPolicyDirective.UnsafeNone => "unsafe-none",
+ CrossOriginEmbedderPolicyDirective.RequireCorp => "require-corp",
+ CrossOriginEmbedderPolicyDirective.Credentialless => "credentialless",
+ _ => throw new NotImplementedException(),
+ };
+
+ HeaderBuilders.Add(new("Cross-Origin-Embedder-Policy", (_, _, _, _) => value));
+ return this;
+ }
+
+
+ ///
+ /// Adds a Cross-Origin-Opener-Policy header.
+ ///
+ /// The COOP directive value.
+ ///
+ public HttpSecurityOptions AddCrossOriginOpenerPolicy(CrossOriginOpenerPolicyDirective directive)
+ {
+ var value = directive switch
+ {
+ CrossOriginOpenerPolicyDirective.UnsafeNone => "unsafe-none",
+ CrossOriginOpenerPolicyDirective.SameOriginAllowPopups => "same-origin-allow-popups",
+ CrossOriginOpenerPolicyDirective.SameOrigin => "same-origin",
+ _ => throw new NotImplementedException(),
+ };
+
+ HeaderBuilders.Add(new("Cross-Origin-Opener-Policy", (_, _, _, _) => value));
+ return this;
+ }
+
+
+ ///
+ /// Adds a Cross-Origin-Resource-Policy header.
+ ///
+ /// The CORP directive value.
+ ///
+ public HttpSecurityOptions AddCrossOriginResourcePolicy(CrossOriginResourcePolicyDirective directive)
+ {
+ var value = directive switch
+ {
+ CrossOriginResourcePolicyDirective.SameSite => "same-site",
+ CrossOriginResourcePolicyDirective.SameOrigin => "same-origin",
+ CrossOriginResourcePolicyDirective.CrossOrigin => "cross-origin",
+ _ => throw new NotImplementedException(),
+ };
+
+ HeaderBuilders.Add(new("Cross-Origin-Resource-Policy", (_, _, _, _) => value));
+ return this;
+ }
+
+
+ ///
+ /// Adds a Reporting-Endpoints header, required for the report-to CSP directive to function.
///
+ /// One or more named endpoint definitions, e.g. ("csp-endpoint", "https://example.com/csp-reports").
///
- public HttpSecurityOptions AddStrictTransportSecurity(ulong maxAgeExpireTime, bool includeSubDomains = false)
+ public HttpSecurityOptions AddReportingEndpoints(params (string name, string url)[] endpoints)
{
- HeaderBuilders.Add(new("Strict-Transport-Security", (_, _, _, _) => $"max-age={maxAgeExpireTime}{(includeSubDomains ? " includeSubDomains" : "")}"));
+ var value = string.Join(", ", endpoints.Select(e => $"{e.name}=\"{e.url}\""));
+ HeaderBuilders.Add(new("Reporting-Endpoints", (_, _, _, _) => value));
return this;
}
diff --git a/HttpSecurity.Example/HttpSecurity.Example.csproj b/HttpSecurity.Example/HttpSecurity.Example.csproj
index 965ef17..b9e6613 100644
--- a/HttpSecurity.Example/HttpSecurity.Example.csproj
+++ b/HttpSecurity.Example/HttpSecurity.Example.csproj
@@ -1,9 +1,10 @@
- net8.0
+ net10.0
enable
enable
+ true
diff --git a/HttpSecurity.Example/Program.cs b/HttpSecurity.Example/Program.cs
index 993d894..0a479dd 100644
--- a/HttpSecurity.Example/Program.cs
+++ b/HttpSecurity.Example/Program.cs
@@ -16,8 +16,6 @@
cspOptions
.AddBaseUri(o => o.AddSelf())
- .AddBlockAllMixedContent()
-
.AddChildSrc(o => o.AddSelf())
.AddConnectSrc(o => o
@@ -40,7 +38,7 @@
.AddFormAction(o => o.AddNone())
.AddImgSrc(o => o
- .AddSelf()
+ .AddSelf()
.AddUri("www.google-analytics.com")
.AddUri("*.openstreetmap.org")
.AddSchemeSource(SchemeSource.Data, "w3.org/svg/2000"))
@@ -54,34 +52,36 @@
.AddReportUri(o => o.AddUri((baseUri, baseDomain) => $"https://{baseUri}/api/CspReporting/UriReport"))
.AddScriptSrc(o => o
- .AddSelf()
.AddNonce()
.AddHashValue(HashAlgorithm.SHA256, "v8v3RKRPmN4odZ1CWM5gw80QKPCCWMcpNeOmimNL2AA=")
- // StrictDynamic works on Chromium browsers but fails for both Firefox and Safari
- //.AddStrictDynamicIf(() => !builder.Environment.IsDevelopment())
+ .AddStrictDynamic()
.AddReportSample()
.AddUri("https://www.googletagmanager.com/gtag/js")
.AddUri((baseUri, baseDomain) => $"https://{baseUri}/_framework/aspnetcore-browser-refresh.js")
.AddUri((baseUri, baseDomain) => $"https://{baseUri}/_framework/blazor.server.js")
.AddGeneratedHashValues(StaticFileExtension.JS))
+ .AddScriptSrcAttr(o => o.AddNone())
+
.AddStyleSrc(o => o
.AddSelf()
.AddUnsafeInline()
- .AddUnsafeHashes()
.AddReportSample())
.AddUpgradeInsecureRequests()
.AddWorkerSrc(o => o.AddSelf());
})
+ .AddCrossOriginOpenerPolicy(CrossOriginOpenerPolicyDirective.SameOrigin)
+ .AddCrossOriginEmbedderPolicy(CrossOriginEmbedderPolicyDirective.RequireCorp)
+ .AddCrossOriginResourcePolicy(CrossOriginResourcePolicyDirective.SameOrigin)
.AddReferrerPolicy(ReferrerPolicyDirective.NoReferrer)
.AddPermissionsPolicy("accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()")
.AddStrictTransportSecurity(31536000, true)
.AddXClientId("HttpSecurity.Example")
.AddXContentTypeOptionsNoSniff()
.AddXFrameOptionsDirective(XFrameOptionsDirective.Deny)
- .AddXXssProtectionDirective(XXssProtectionDirective.OneModeBlock)
+ .AddXXssProtectionDirective(XXssProtectionDirective.Zero)
.AddXPermittedCrossDomainPoliciesDirective(XPermittedCrossDomainPoliciesDirective.None);
},
onStartingOptions =>
@@ -112,10 +112,10 @@
app.UseHttpSecurityHeaders();
-app.UseStaticFiles();
-
app.UseRouting();
+app.MapStaticAssets();
+
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
diff --git a/HttpSecurity.Example/Properties/launchSettings.json b/HttpSecurity.Example/Properties/launchSettings.json
index b7eca4f..8c25c30 100644
--- a/HttpSecurity.Example/Properties/launchSettings.json
+++ b/HttpSecurity.Example/Properties/launchSettings.json
@@ -8,7 +8,7 @@
},
"hotReloadEnabled": false,
"dotnetRunMessages": true,
- "applicationUrl": "https://localhost:50083",
+ "applicationUrl": "https://localhost:50083"
}
}
-}
\ No newline at end of file
+}
diff --git a/HttpSecurity.Example/appsettings.json b/HttpSecurity.Example/appsettings.json
index 10f68b8..f8930b6 100644
--- a/HttpSecurity.Example/appsettings.json
+++ b/HttpSecurity.Example/appsettings.json
@@ -1,4 +1,5 @@
{
+ "ReloadStaticAssetsAtRuntime": false,
"Logging": {
"LogLevel": {
"Default": "Information",
diff --git a/SourceGenerator/SourceGenerator.cs b/SourceGenerator/SourceGenerator.cs
index e86a841..ca07558 100644
--- a/SourceGenerator/SourceGenerator.cs
+++ b/SourceGenerator/SourceGenerator.cs
@@ -2,6 +2,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
+using System.Collections.Immutable;
#if DEBUG && GENERATOR_DEBUG
using System.Diagnostics;
#endif
@@ -11,9 +12,9 @@
namespace HttpSecurity.AspNet;
[Generator]
-internal class SourceGenerator : ISourceGenerator
+internal class SourceGenerator : IIncrementalGenerator
{
- public void Initialize(GeneratorInitializationContext context)
+ public void Initialize(IncrementalGeneratorInitializationContext context)
{
#if DEBUG && GENERATOR_DEBUG
if (!Debugger.IsAttached)
@@ -22,33 +23,31 @@ public void Initialize(GeneratorInitializationContext context)
}
#endif
- context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
+ var classDeclarations = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ predicate: static (node, _) => node is ClassDeclarationSyntax,
+ transform: static (ctx, _) => (ClassDeclarationSyntax)ctx.Node)
+ .Collect();
+
+ var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations);
+
+ context.RegisterSourceOutput(compilationAndClasses, static (spc, source) =>
+ Execute(source.Left, source.Right, spc));
}
- private readonly string[] _policyOptionAdditionalAttributes = { "AddNone", "AddReportSample", "AddScript", "AddSelf", "AddStrictDynamic", "AddUnsafeEval", "AddUnsafeHashes", "AddUnsafeInline" };
+ private static readonly string[] _policyOptionAdditionalAttributes = { "AddNone", "AddReportSample", "AddScript", "AddSelf", "AddStrictDynamic", "AddUnsafeEval", "AddUnsafeHashes", "AddUnsafeInline" };
- public void Execute(GeneratorExecutionContext context)
+ private static void Execute(Compilation compilation, ImmutableArray classes, SourceProductionContext context)
{
Extensions.LinesGenerated = 0;
- // retreive the populated receiver
- if (context.SyntaxReceiver is not SyntaxReceiver receiver)
- {
- return;
- }
-
- // we're going to create a new compilation that contains the attribute.
- // TODO: we should allow source generators to provide source during initialize, so that this step isn't required.
- //CSharpParseOptions options = (context.Compilation as CSharpCompilation).SyntaxTrees[0].Options as CSharpParseOptions;
- Compilation compilation = context.Compilation;
-
List policyClassSymbols = new();
Dictionary policyOptionClassSymbols = new();
INamedTypeSymbol contentSecurityPolicyOptionsClassSymbol = null;
- foreach (var classNode in receiver.Classes)
+ foreach (var classNode in classes)
{
var modifiers = classNode.Modifiers.Select(m => m.Text).ToList();
SemanticModel classModel = compilation.GetSemanticModel(classNode.SyntaxTree);
@@ -110,7 +109,7 @@ public void Execute(GeneratorExecutionContext context)
}
- private StringBuilder ProcessContentSecurityPolicyOptions(INamedTypeSymbol contentSecurityPolicyOptionsClassSymbol, List policyClassSymbols, Dictionary policyOptionClassSymbols)
+ private static StringBuilder ProcessContentSecurityPolicyOptions(INamedTypeSymbol contentSecurityPolicyOptionsClassSymbol, List policyClassSymbols, Dictionary policyOptionClassSymbols)
{
StringBuilder sb = new();
var isFirst = true;
@@ -174,7 +173,7 @@ private StringBuilder ProcessContentSecurityPolicyOptions(INamedTypeSymbol conte
}
- private bool ProcessPolicyAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
+ private static bool ProcessPolicyAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
{
var attribute = classSymbol.GetAttributes().Where(ad => ad.AttributeClass.Name == $"ContentSecurityPolicyAttribute").FirstOrDefault();
@@ -219,7 +218,7 @@ private bool ProcessPolicyAttribute(INamedTypeSymbol classSymbol, StringBuilder
}
- private bool ProcessPolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
+ private static bool ProcessPolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
{
var attribute = classSymbol.GetAttributes().Where(ad => ad.AttributeClass.Name == $"ContentSecurityPolicyOptionsAttribute").FirstOrDefault();
@@ -256,7 +255,7 @@ private bool ProcessPolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringB
}
- private bool ProcessAdditionalPolicyOptionsAttribute(INamedTypeSymbol classSymbol, string attributeName, StringBuilder sb)
+ private static bool ProcessAdditionalPolicyOptionsAttribute(INamedTypeSymbol classSymbol, string attributeName, StringBuilder sb)
{
var attribute = classSymbol.GetAttributes().Where(ad => ad.AttributeClass.Name == $"{GetLongAttributeName(attributeName)}").FirstOrDefault();
@@ -298,7 +297,7 @@ private bool ProcessAdditionalPolicyOptionsAttribute(INamedTypeSymbol classSymbo
}
- private bool ProcessGroupNamePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
+ private static bool ProcessGroupNamePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
{
var attribute = classSymbol.GetAttributes().Where(ad => ad.AttributeClass.Name == $"{GetLongAttributeName("AddGroupName")}").FirstOrDefault();
@@ -338,7 +337,7 @@ private bool ProcessGroupNamePolicyOptionsAttribute(INamedTypeSymbol classSymbol
}
- private bool ProcessHashValuePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
+ private static bool ProcessHashValuePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
{
var attribute = classSymbol.GetAttributes().Where(ad => ad.AttributeClass.Name == $"{GetLongAttributeName("AddHashValue")}").FirstOrDefault();
@@ -416,7 +415,7 @@ private bool ProcessHashValuePolicyOptionsAttribute(INamedTypeSymbol classSymbol
}
- private bool ProcessHostSourcePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
+ private static bool ProcessHostSourcePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
{
var attribute = classSymbol.GetAttributes().Where(ad => ad.AttributeClass.Name == $"{GetLongAttributeName("AddHostSourceValue")}").FirstOrDefault();
@@ -456,7 +455,7 @@ private bool ProcessHostSourcePolicyOptionsAttribute(INamedTypeSymbol classSymbo
}
- private bool ProcessNoncePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
+ private static bool ProcessNoncePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
{
var attribute = classSymbol.GetAttributes().Where(ad => ad.AttributeClass.Name == $"{GetLongAttributeName("AddNonce")}").FirstOrDefault();
@@ -496,7 +495,7 @@ private bool ProcessNoncePolicyOptionsAttribute(INamedTypeSymbol classSymbol, St
}
- private bool ProcessPolicyNamePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
+ private static bool ProcessPolicyNamePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
{
var attribute = classSymbol.GetAttributes().Where(ad => ad.AttributeClass.Name == $"{GetLongAttributeName("AddPolicyName")}").FirstOrDefault();
@@ -536,7 +535,7 @@ private bool ProcessPolicyNamePolicyOptionsAttribute(INamedTypeSymbol classSymbo
}
- private bool ProcessSchemeSourcePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
+ private static bool ProcessSchemeSourcePolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
{
var attribute = classSymbol.GetAttributes().Where(ad => ad.AttributeClass.Name == $"{GetLongAttributeName("AddSchemeSource")}").FirstOrDefault();
@@ -604,7 +603,7 @@ private bool ProcessSchemeSourcePolicyOptionsAttribute(INamedTypeSymbol classSym
}
- private bool ProcessUriPolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
+ private static bool ProcessUriPolicyOptionsAttribute(INamedTypeSymbol classSymbol, StringBuilder sb)
{
var attribute = classSymbol.GetAttributes().Where(ad => ad.AttributeClass.Name == $"{GetLongAttributeName("AddUri")}").FirstOrDefault();
@@ -671,38 +670,6 @@ private bool ProcessUriPolicyOptionsAttribute(INamedTypeSymbol classSymbol, Stri
}
- ///
- /// Created on demand before each generation pass
- ///
- class SyntaxReceiver : ISyntaxReceiver
- {
- /////
- ///// Dictionary keyed by class nodes that have a ViewModelRecord attribute and with value being a list of
- ///// properties with the ViewModelProperty attribute in that record.
- /////
- //public readonly Dictionary> ClassNodes = new();
-
-
- ///
- /// List of classes with the ViewModelRecord attribute. Diagnostic reporting will be created for these classes
- /// because the attribute is for partial records only.
- ///
- public readonly List Classes = new();
-
-
- ///
- /// Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation
- ///
- public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
- {
- // any field with at least one attribute is a candidate for property generation
- if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax)
- {
- Classes.Add(classDeclarationSyntax);
- }
- }
- }
-
private static string GetClassTypeName(INamedTypeSymbol classSymbol, bool suppressGeneric = false)
{
diff --git a/SourceGenerator/SourceGenerator.csproj b/SourceGenerator/SourceGenerator.csproj
index def7e04..520422a 100644
--- a/SourceGenerator/SourceGenerator.csproj
+++ b/SourceGenerator/SourceGenerator.csproj
@@ -2,7 +2,7 @@
netstandard2.0
- 11
+ latest
true
@@ -13,11 +13,11 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/dotnet10.sln b/dotnet10.sln
new file mode 100644
index 0000000..7698312
--- /dev/null
+++ b/dotnet10.sln
@@ -0,0 +1,36 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpSecurity.Example", "HttpSecurity.Example\HttpSecurity.Example.csproj", "{1A25AA86-DDFB-38A7-3371-7793AC13C922}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGenerator", "SourceGenerator\SourceGenerator.csproj", "{DA39BAE9-51E6-F712-D052-5DF9B72D3A9A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpSecurity.AspNet", "HttpSecurity.AspNet\HttpSecurity.AspNet.csproj", "{6704D854-500D-5569-047F-126DD5B5C9C5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1A25AA86-DDFB-38A7-3371-7793AC13C922}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1A25AA86-DDFB-38A7-3371-7793AC13C922}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1A25AA86-DDFB-38A7-3371-7793AC13C922}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1A25AA86-DDFB-38A7-3371-7793AC13C922}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DA39BAE9-51E6-F712-D052-5DF9B72D3A9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DA39BAE9-51E6-F712-D052-5DF9B72D3A9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DA39BAE9-51E6-F712-D052-5DF9B72D3A9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DA39BAE9-51E6-F712-D052-5DF9B72D3A9A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6704D854-500D-5569-047F-126DD5B5C9C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6704D854-500D-5569-047F-126DD5B5C9C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6704D854-500D-5569-047F-126DD5B5C9C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6704D854-500D-5569-047F-126DD5B5C9C5}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {0714180F-EBEB-417C-9AF6-299BE531DBE0}
+ EndGlobalSection
+EndGlobal
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..53b4c27
--- /dev/null
+++ b/global.json
@@ -0,0 +1,7 @@
+{
+ "sdk": {
+ "version": "10.0.103",
+ "rollForward": "latestFeature",
+ "allowPrerelease": false
+ }
+}