Skip to content
Merged
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
28 changes: 27 additions & 1 deletion samples/WebSample/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
using PowerCSharp.Feature.Cache;
using PowerCSharp.Feature.Cache.BitFaster;
using PowerCSharp.Feature.Sanitization;
using PowerCSharp.Features;

namespace WebSample.Extensions;

/// <summary>
Expand All @@ -15,7 +20,7 @@ public static IServiceCollection AddSwaggerServices(this IServiceCollection serv
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new() { Title = "PowerCSharp Web Sample API", Version = "v1" });

// Include XML comments
var xmlFile = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
Expand All @@ -24,4 +29,25 @@ public static IServiceCollection AddSwaggerServices(this IServiceCollection serv

return services;
}

/// <summary>
/// Registers the PowerCSharp Features Framework via auto-discovery, opting in the Cache and
/// Sanitization feature modules. Also registers the BitFaster cache provider so the Cache demo
/// endpoint has an active backend rather than the NoOp floor.
/// </summary>
/// <param name="services">The IServiceCollection instance</param>
/// <param name="configuration">The application configuration</param>
public static IServiceCollection AddPowerCSharpFeatures(this IServiceCollection services, IConfiguration configuration)
{
services.AddPowerFeatures(configuration, options =>
{
options.ScanAssemblies(
typeof(CacheFeatureModule).Assembly,
typeof(SanitizationFeatureModule).Assembly);
});

services.AddCacheBitFaster(configuration);

return services;
}
}
17 changes: 17 additions & 0 deletions samples/WebSample/Extensions/WebApplicationExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using PowerCSharp.Features;
using WebSample.Samples.Core;
using WebSample.Samples.Extensions;
using WebSample.Samples.Features;
using WebSample.Samples.Helpers;
using WebSample.Samples.Utilities;

Expand All @@ -24,6 +26,17 @@ public static void UseSwaggerUI(this WebApplication app)
});
}

/// <summary>
/// Runs the PowerCSharp Features Framework pipeline hook — resolves each enabled feature's
/// middleware (none, for Cache/Sanitization) and bridges the Sanitization engine's
/// configuration for non-DI call sites.
/// </summary>
/// <param name="app">The WebApplication instance</param>
public static void UsePowerCSharpFeatures(this WebApplication app)
{
app.UsePowerFeatures();
}

/// <summary>
/// Maps all PowerCSharp demo endpoints using dedicated endpoint classes
/// </summary>
Expand Down Expand Up @@ -55,5 +68,9 @@ public static void MapPowerCSharpDemoEndpoints(this WebApplication app)
app.MapGet("/demo/collection", CollectionSampleEndpoints.GetDemoData);
app.MapGet("/demo/dictionary", DictionarySampleEndpoints.GetDemoData);
app.MapGet("/demo/unix-timestamp", UnixTimestampSampleEndpoints.GetDemoData);

// Features Framework Samples
app.MapGet("/demo/cache", CacheSampleEndpoints.GetDemoDataAsync);
app.MapGet("/demo/sanitization", SanitizationSampleEndpoints.GetDemoData);
}
}
6 changes: 6 additions & 0 deletions samples/WebSample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
// Add Swagger services
builder.Services.AddSwaggerServices();

// Add the PowerCSharp Features Framework (Cache + Sanitization feature modules)
builder.Services.AddPowerCSharpFeatures(builder.Configuration);

var app = builder.Build();

// Run the Features Framework pipeline hook (bridges the Sanitization engine's configuration)
app.UsePowerCSharpFeatures();

// Map all PowerCSharp demo endpoints
app.MapPowerCSharpDemoEndpoints();

Expand Down
39 changes: 39 additions & 0 deletions samples/WebSample/Samples/Features/CacheSampleEndpoints.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using PowerCSharp.Feature.Cache.Abstractions;

namespace WebSample.Samples.Features;

/// <summary>
/// Sample endpoint demonstrating the Cache feature, resolved via the Features Framework
/// (<c>PowerCSharp.Feature.Cache</c> + <c>PowerCSharp.Feature.Cache.BitFaster</c>).
/// </summary>
public static class CacheSampleEndpoints
{
private const string DemoKey = "websample:cache:demo";

/// <summary>
/// Gets cache demo data: a cache miss, a write, and the resulting hit, using the
/// DI-resolved <see cref="ICacheService"/> (the active BitFaster provider, or NoOp if the
/// Cache feature is disabled via configuration).
/// </summary>
/// <param name="cache">The cache service, injected by the Features Framework.</param>
/// <returns>Demo results showing a cache miss followed by a set/hit round-trip.</returns>
public static async Task<object> GetDemoDataAsync(ICacheService cache)
{
// Start from a clean slate so this endpoint is idempotent across repeated calls.
await cache.RemoveAsync(DemoKey);

var missResult = await cache.GetWithResultAsync<string>(DemoKey);

var value = $"cached at {DateTimeOffset.UtcNow:O}";
await cache.SetAsync(DemoKey, value, TimeSpan.FromMinutes(1));

var hitResult = await cache.GetWithResultAsync<string>(DemoKey);

return new
{
providerNote = "Backed by PowerCSharp.Feature.Cache.BitFaster; falls back to a NoOp cache if the Cache feature is disabled.",
beforeSet = new { hit = missResult.IsSuccess, value = missResult.Value },
afterSet = new { hit = hitResult.IsSuccess, value = hitResult.Value, provider = hitResult.ProviderName }
};
}
}
57 changes: 57 additions & 0 deletions samples/WebSample/Samples/Features/SanitizationSampleEndpoints.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using PowerCSharp.Feature.Sanitization.Abstractions;

namespace WebSample.Samples.Features;

/// <summary>
/// Sample endpoint demonstrating the Sanitization feature, resolved via the Features Framework
/// (<c>PowerCSharp.Feature.Sanitization</c>).
/// </summary>
public static class SanitizationSampleEndpoints
{
/// <summary>
/// Gets sanitization demo data covering all four concerns: log injection (CWE-117),
/// file-path traversal (CWE-22), sensitive-data masking (CWE-200), and regex-injection/ReDoS
/// validation (CWE-400/CWE-730), using the DI-resolved <see cref="ISanitizationService"/>.
/// </summary>
/// <param name="sanitizer">The sanitization service, injected by the Features Framework.</param>
/// <returns>Demo results for each sanitization concern.</returns>
public static object GetDemoData(ISanitizationService sanitizer)
{
const string maliciousLogInput = "user logged in\r\nADMIN: fake audit entry injected";
const string maliciousPath = "../../etc/passwd";
const string secretPayload = "password=SuperSecret123, token=abcdef0123456789";
const string unsafeRegexPattern = "(a+)+$"; // classic catastrophic-backtracking shape

var logResult = sanitizer.SanitizeForLogInjection(maliciousLogInput);
var pathResult = sanitizer.SanitizeForFilePath(maliciousPath);
var sensitiveResult = sanitizer.SanitizeForSensitiveData(secretPayload);
var regexResult = sanitizer.SanitizeForRegexInjection(unsafeRegexPattern);

return new
{
note = "Falls back to a NoOp sanitizer (inputs pass through unchanged) if the Sanitization feature is disabled.",
logInjection = new
{
original = maliciousLogInput,
sanitized = logResult.SanitizedValue,
wasModified = logResult.WasModified
},
filePathTraversal = new
{
original = maliciousPath,
wasRejected = pathResult.IsRejected
},
sensitiveDataMasking = new
{
original = secretPayload,
masked = sensitiveResult.SanitizedValue,
wasModified = sensitiveResult.WasModified
},
regexInjection = new
{
original = unsafeRegexPattern,
wasRejected = regexResult.IsRejected
}
};
}
}
7 changes: 7 additions & 0 deletions samples/WebSample/WebSample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
<ProjectReference Include="..\..\src\PowerCSharp.Extensions.AspNetCore\PowerCSharp.Extensions.AspNetCore.csproj" />
<ProjectReference Include="..\..\src\PowerCSharp.Utilities\PowerCSharp.Utilities.csproj" />
<ProjectReference Include="..\..\src\PowerCSharp.Helpers\PowerCSharp.Helpers.csproj" />
<ProjectReference Include="..\..\src\Features\PowerCSharp.Features.Abstractions\PowerCSharp.Features.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Features\PowerCSharp.Features\PowerCSharp.Features.csproj" />
<ProjectReference Include="..\..\src\Features\PowerCSharp.Feature.Cache.Abstractions\PowerCSharp.Feature.Cache.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Features\PowerCSharp.Feature.Cache\PowerCSharp.Feature.Cache.csproj" />
<ProjectReference Include="..\..\src\Features\PowerCSharp.Feature.Cache.BitFaster\PowerCSharp.Feature.Cache.BitFaster.csproj" />
<ProjectReference Include="..\..\src\Features\PowerCSharp.Feature.Sanitization.Abstractions\PowerCSharp.Feature.Sanitization.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Features\PowerCSharp.Feature.Sanitization\PowerCSharp.Feature.Sanitization.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
12 changes: 11 additions & 1 deletion samples/WebSample/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,15 @@
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"PowerFeatures": {
"Cache": {
"Enabled": true,
"Provider": "BitFaster",
"Capacity": 1000
},
"Sanitization": {
"Enabled": true
}
}
}
Loading