Skip to content

Commit 799ee50

Browse files
sergey-tihonThoriumCopilot
authored
SSRF for IP V6 and Tests (#272)
* Server-Side Request Forgery (SSRF) protection * Update src/SwaggerProvider.DesignTime/Utils.fs Co-authored-by: Copilot <[email protected]> * Update src/SwaggerProvider.DesignTime/Utils.fs Co-authored-by: Copilot <[email protected]> * Copilot feedback implemented, and formatted with Fantomas * Content validation improved * refact: pattern matching * fix: remove duplicated condition * fix: handle relative file paths that expand to absolute paths The readSchemaPath function now properly handles relative file paths (e.g., those created with __SOURCE_DIRECTORY__) by attempting to resolve them to absolute paths before treating them as remote URLs. This ensures paths like '__SOURCE_DIRECTORY__ + "/../Schemas/v2/petstore.json"' are correctly recognized as local files instead of being rejected by SSRF validation. * refactor: consolidate readSchemaPath and getAbsolutePath functions Merge path resolution into readSchemaPath to eliminate redundant calls and improve code clarity: - Move getAbsolutePath logic into readSchemaPath, making it fully self-contained - Remove dead code path that attempted to re-resolve with empty resolutionFolder - Rename Cache modules to SwaggerCache and OpenApiCache to avoid naming conflicts - Update test calls to pass resolutionFolder parameter - All 274 tests pass (116 unit + 158 integration) * Apply suggestion from @Copilot Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Tuomas Hietanen <[email protected]> Co-authored-by: Tuomas Hietanen <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 942d80c commit 799ee50

File tree

7 files changed

+638
-151
lines changed

7 files changed

+638
-151
lines changed

.config/dotnet-tools.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"rollForward": false
1818
},
1919
"fantomas": {
20-
"version": "7.0.0",
20+
"version": "7.0.3",
2121
"commands": [
2222
"fantomas"
2323
],

AGENTS.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
## Build, Test & Lint Commands
2+
3+
- **Build**: `dotnet fake build -t Build` (Release configuration)
4+
- **Format Check**: `dotnet fake build -t CheckFormat` (validates Fantomas formatting)
5+
- **Format**: `dotnet fake build -t Format` (applies Fantomas formatting)
6+
- **All Tests**: `dotnet fake build -t RunTests` (builds + starts test server + runs all tests)
7+
- **Unit Tests Only**: `dotnet build && dotnet tests/SwaggerProvider.Tests/bin/Release/net9.0/SwaggerProvider.Tests.dll`
8+
- **Provider Tests (Integration)**:
9+
1. Build test server: `dotnet build tests/Swashbuckle.WebApi.Server/Swashbuckle.WebApi.Server.fsproj -c Release`
10+
2. Start server in background: `dotnet tests/Swashbuckle.WebApi.Server/bin/Release/net9.0/Swashbuckle.WebApi.Server.dll`
11+
3. Build tests: `dotnet build SwaggerProvider.TestsAndDocs.sln -c Release`
12+
4. Run tests: `dotnet tests/SwaggerProvider.ProviderTests/bin/Release/net9.0/SwaggerProvider.ProviderTests.dll`
13+
- **Single Test**: Run via xunit runner: `dotnet [assembly] [filter]`
14+
15+
## Code Style Guidelines
16+
17+
**Language**: F# (net9.0 target framework)
18+
19+
**Imports & Namespaces**:
20+
21+
- `namespace [Module]` at file start; no `open` statements at module level
22+
- Use `module [Name]` for nested modules
23+
- Open dependencies after namespace declaration (e.g., `open Xunit`, `open FsUnitTyped`)
24+
- Fully qualify internal modules: `SwaggerProvider.Internal.v2.Parser`, `SwaggerProvider.Internal.v3.Compilers`
25+
26+
**Formatting** (via Fantomas, EditorConfig enforced):
27+
28+
- 4-space indents, max 150 char line length
29+
- `fsharp_max_function_binding_width=10`, `fsharp_max_infix_operator_expression=70`
30+
- No space before parameter/lowercase invocation
31+
- Multiline block brackets on same column, Stroustrup style enabled
32+
- Bar before discriminated union declarations, max 3 blank lines
33+
34+
**Naming Conventions**:
35+
36+
- PascalCase for classes, types, modules, public members
37+
- camelCase for local/private bindings, parameters
38+
- Suffix test functions with `Tests` or use attributes like `[<Theory>]`, `[<Fact>]`
39+
40+
**Type Annotations**:
41+
42+
- Explicit return types for public functions (recommended)
43+
- Use type inference for local bindings when obvious
44+
- Generic type parameters: `'a`, `'b` (single quote prefix)
45+
46+
**Error Handling**:
47+
48+
- Use `Result<'T, 'Error>` or `Option<'T>` for fallible operations
49+
- `failwith` or `failwithf` for errors in type providers and compilers
50+
- Task-based async for I/O: `task { }` expressions in tests
51+
- Match failures with `| _ -> ...` or pattern guards with `when`
52+
53+
**File Organization**:
54+
55+
- Tests use Xunit attributes: `[<Theory>]`, `[<Fact>]`, `[<MemberData>]`
56+
- Design-time providers in `src/SwaggerProvider.DesignTime/`, runtime in `src/SwaggerProvider.Runtime/`
57+
- Test schemas organized by OpenAPI version: `tests/.../Schemas/{v2,v3}/`
58+
59+
## Key Patterns
60+
61+
- Type Providers use `ProvidedApiClientBase` and compiler pipeline (DefinitionCompiler, OperationCompiler)
62+
- SSRF protection enabled by default; disable with `SsrfProtection=false` static parameter
63+
- Target net9.0; use implicit async/await (task expressions)

src/SwaggerProvider.DesignTime/Provider.OpenApiClient.fs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ open Swagger
88
open SwaggerProvider.Internal
99
open SwaggerProvider.Internal.v3.Compilers
1010

11-
module Cache =
11+
module OpenApiCache =
1212
let providedTypes = Caching.createInMemoryCache(TimeSpan.FromSeconds 30.0)
1313

1414
/// The Open API Provider.
@@ -51,25 +51,21 @@ type public OpenApiClientTypeProvider(cfg: TypeProviderConfig) as this =
5151
t.DefineStaticParameters(
5252
staticParams,
5353
fun typeName args ->
54-
let schemaPath =
55-
let schemaPathRaw = unbox<string> args.[0]
56-
SchemaReader.getAbsolutePath cfg.ResolutionFolder schemaPathRaw
57-
54+
let schemaPathRaw = unbox<string> args.[0]
5855
let ignoreOperationId = unbox<bool> args.[1]
5956
let ignoreControllerPrefix = unbox<bool> args.[2]
6057
let preferNullable = unbox<bool> args.[3]
6158
let preferAsync = unbox<bool> args.[4]
6259
let ssrfProtection = unbox<bool> args.[5]
6360

6461
let cacheKey =
65-
(schemaPath, ignoreOperationId, ignoreControllerPrefix, preferNullable, preferAsync, ssrfProtection)
62+
(schemaPathRaw, ignoreOperationId, ignoreControllerPrefix, preferNullable, preferAsync, ssrfProtection)
6663
|> sprintf "%A"
6764

68-
6965
let addCache() =
7066
lazy
7167
let schemaData =
72-
SchemaReader.readSchemaPath (not ssrfProtection) "" schemaPath
68+
SchemaReader.readSchemaPath (not ssrfProtection) "" cfg.ResolutionFolder schemaPathRaw
7369
|> Async.RunSynchronously
7470

7571
let openApiReader = Microsoft.OpenApi.Readers.OpenApiStringReader()
@@ -96,18 +92,18 @@ type public OpenApiClientTypeProvider(cfg: TypeProviderConfig) as this =
9692
let ty =
9793
ProvidedTypeDefinition(tempAsm, ns, typeName, Some typeof<obj>, isErased = false, hideObjectMethods = true)
9894

99-
ty.AddXmlDoc("OpenAPI Provider for " + schemaPath)
95+
ty.AddXmlDoc("OpenAPI Provider for " + schemaPathRaw)
10096
ty.AddMembers tys
10197
tempAsm.AddTypes [ ty ]
10298

10399
ty
104100

105101
try
106-
Cache.providedTypes.GetOrAdd(cacheKey, addCache).Value
102+
OpenApiCache.providedTypes.GetOrAdd(cacheKey, addCache).Value
107103
with _ ->
108-
Cache.providedTypes.Remove(cacheKey) |> ignore
104+
OpenApiCache.providedTypes.Remove(cacheKey) |> ignore
109105

110-
Cache.providedTypes.GetOrAdd(cacheKey, addCache).Value
106+
OpenApiCache.providedTypes.GetOrAdd(cacheKey, addCache).Value
111107
)
112108

113109
t

src/SwaggerProvider.DesignTime/Provider.SwaggerClient.fs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ open SwaggerProvider.Internal
99
open SwaggerProvider.Internal.v2.Parser
1010
open SwaggerProvider.Internal.v2.Compilers
1111

12+
module SwaggerCache =
13+
let providedTypes = Caching.createInMemoryCache(TimeSpan.FromSeconds 30.0)
14+
1215
/// The Swagger Type Provider.
1316
[<TypeProvider; Obsolete("Use OpenApiClientTypeProvider when possible, it supports v2 & v3 schema formats.")>]
1417
type public SwaggerTypeProvider(cfg: TypeProviderConfig) as this =
@@ -51,10 +54,7 @@ type public SwaggerTypeProvider(cfg: TypeProviderConfig) as this =
5154
t.DefineStaticParameters(
5255
staticParams,
5356
fun typeName args ->
54-
let schemaPath =
55-
let schemaPathRaw = unbox<string> args.[0]
56-
SchemaReader.getAbsolutePath cfg.ResolutionFolder schemaPathRaw
57-
57+
let schemaPathRaw = unbox<string> args.[0]
5858
let headersStr = unbox<string> args.[1]
5959
let ignoreOperationId = unbox<bool> args.[2]
6060
let ignoreControllerPrefix = unbox<bool> args.[3]
@@ -63,13 +63,13 @@ type public SwaggerTypeProvider(cfg: TypeProviderConfig) as this =
6363
let ssrfProtection = unbox<bool> args.[6]
6464

6565
let cacheKey =
66-
(schemaPath, headersStr, ignoreOperationId, ignoreControllerPrefix, preferNullable, preferAsync, ssrfProtection)
66+
(schemaPathRaw, headersStr, ignoreOperationId, ignoreControllerPrefix, preferNullable, preferAsync, ssrfProtection)
6767
|> sprintf "%A"
6868

6969
let addCache() =
7070
lazy
7171
let schemaData =
72-
SchemaReader.readSchemaPath (not ssrfProtection) headersStr schemaPath
72+
SchemaReader.readSchemaPath (not ssrfProtection) headersStr cfg.ResolutionFolder schemaPathRaw
7373
|> Async.RunSynchronously
7474

7575
let schema = SwaggerParser.parseSchema schemaData
@@ -87,13 +87,13 @@ type public SwaggerTypeProvider(cfg: TypeProviderConfig) as this =
8787
let ty =
8888
ProvidedTypeDefinition(tempAsm, ns, typeName, Some typeof<obj>, isErased = false, hideObjectMethods = true)
8989

90-
ty.AddXmlDoc("Swagger Provider for " + schemaPath)
90+
ty.AddXmlDoc("Swagger Provider for " + schemaPathRaw)
9191
ty.AddMembers tys
9292
tempAsm.AddTypes [ ty ]
9393

9494
ty
9595

96-
Cache.providedTypes.GetOrAdd(cacheKey, addCache).Value
96+
SwaggerCache.providedTypes.GetOrAdd(cacheKey, addCache).Value
9797
)
9898

9999
t

0 commit comments

Comments
 (0)