Security Disclosure
This page tracks the security hardening work delivered across Sharkable core, Sharkable.Cache.Redis, and Sharkable.AutoCrud.SqlSugar. Each entry summarizes the fix; for the full audit context, see local/security-audit.md in each repository.
Core library (Sharkable)
| ID | Summary |
|---|---|
| SHARK-SEC-003 | Replace Thread.Sleep with await Task.Delay in graceful shutdown drain — prevent ApplicationStopping thread block |
| SHARK-SEC-004 | Make SagaExecutor.LockTtl configurable + add LockRenewalInterval with periodic lock extension — prevent split-brain sagas when step duration exceeds lock TTL |
| SHARK-SEC-005 | Replace unconditional KeyDelete with check-and-delete Lua in RedisSagaStore.ReleaseLockAsync / RedisCronJobStore.ReleaseJobLockAsync; add CronScheduler.CronLockTtl + ICronJobStore.RenewJobLockAsync — prevent split-brain when LockTtl expires mid-work (cross-repo with Sharkable.Cache.Redis) |
| SHARK-SEC-006 | BREAKING — Add [CrudAllow] attribute for explicit field allowlist on AutoCrud insert/update; endpoints with Create | Update and zero [CrudAllow] properties now throw InvalidOperationException at startup. Prevents mass-assignment privilege escalation via JSON body (cross-repo with Sharkable.AutoCrud.SqlSugar) |
| SHARK-SEC-007 | Add JWT algorithm allowlist + RequireSignedTokens + RequireExpirationTime + reduce ClockSkew to 30s — prevent algorithm confusion attacks |
| SHARK-SEC-008 | Use CryptographicOperations.FixedTimeEquals for API key comparison — prevent timing oracle |
| SHARK-SEC-009 | Gate ScalarJwtToken / ScalarApiKeyValue to IHostEnvironment.IsDevelopment() — prevent token leakage to public /scalar/v1 UI |
| SHARK-SEC-010 | Implement AuditTrailMiddleware header redaction per RedactHeaders list — credential-bearing headers (Authorization, X-Api-Key, Cookie by default) have their values replaced with *** in audit log output |
| SHARK-SEC-011 | Add SharkOption.RequireAuthenticatedByDefault opt-in flag — enforce auth on all endpoints via per-endpoint [Authorize] metadata injection |
| SHARK-SEC-012 | Add ETagOptions.MaxResponseSize (default 10 MiB) + counting stream + incremental hashing — prevent OOM via huge response bodies |
| SHARK-SEC-013 | Add IMemoryCache SizeLimit (100k) + periodic eviction sweep to MemoryRateLimitStore — prevent slow-loris DoS via unique path explosion |
| SHARK-SEC-014 | Add IMemoryCache SizeLimit (10k) + entry.Size tracking to MemoryIdempotencyStore — prevent TB-DoS via unique idempotency keys |
| SHARK-SEC-015 | Require API key on profiler endpoint /_sharkable/profiler by default; return 404 if no API keys configured; cap top slow-requests surface at 50 |
| SHARK-SEC-016 | Require API key on cron admin endpoint /_sharkable/jobs; redact LastError field to its first 100 characters + ... to prevent business-logic leakage |
| SHARK-SEC-017 | BREAKING — Make CronScheduler.Register async; ICronScheduler.Register replaced by RegisterAsync returning Task — eliminate sync-over-async deadlock risk with distributed stores |
| SHARK-SEC-028 | Pin Microsoft.OpenApi to 2.7.5 — suppress NU1903 (CVE-2026-49451, circular schema stack overflow) |
Plugin: Sharkable.Cache.Redis
| ID | Summary |
|---|---|
| SHARK-SEC-018 | Redact RedisHealthCheck description — never expose endpoint topology or ex.Message on public /healthz; full diagnostic detail logged at LogWarning for operators only |
| SHARK-SEC-019 | Validate connection string at AddSharkableRedis — null-check + default abortConnect=false + optional TLS enforcement via RedisStoreOptions.RequireTls |
| SHARK-SEC-020 | Replace empty catch in RedisIdempotencyStore.GetAsync with typed exception filter + structured LogWarning + re-throw wrapped in InvalidOperationException — prevent silent double-execution when a corrupted record is encountered |
| SHARK-SEC-021 | Add UseSharkableRedisHealthCheck() extension — explicit opt-in to wire RedisHealthCheck into HealthCheckService under name "redis" and tag "ready". The check is no longer auto-surfaced on /healthz by default |
| SHARK-SEC-022 | Set TTL on RedisSagaStore.SaveProgressAsync via RedisStoreOptions.SagaProgressTtl (default 7d) — prevent unbounded Redis key growth from orphaned saga progress records after host crashes |
Plugin: Sharkable.AutoCrud.SqlSugar
| ID | Summary |
|---|---|
| SHARK-SEC-023 | Add AutoCrudSqlSugar.AutoCrudRequireAuthorization opt-in flag — auto-attach .RequireAuthorization() to every generated CRUD endpoint when true. Default false preserves backward compat, but production deployments MUST enable. AddSqlSugar() logs a LogWarning at startup when the flag is false in non-AOT mode |
| SHARK-SEC-024 | Defense-in-depth: also exclude SafeSoftDeleteField from the [CrudAllow] allow-list by case-insensitive name match — protects entities whose C# property name matches the configured soft-delete column but lacks a [SugarColumn] rename |
| SHARK-SEC-025 | Add AutoCrudSqlSugar.MaxPageNumber (default 1M) + overflow check on (page - 1) * pageSize — prevent pagination DoS via page=int.MaxValue producing a negative OFFSET |
| SHARK-SEC-026 | Redact SqlSugarHealthCheck description — never expose dbType or ex.Message on public /healthz; full diagnostic detail (DB type, exception) logged at LogWarning for operators only |
| SHARK-SEC-027 | Escape SQL LIKE wildcards (%, _, \) with ESCAPE '\' clause + cap filter value length via AutoCrudSqlSugar.MaxFilterValueLength (default 200) + cap IN / NOT IN array size via AutoCrudSqlSugar.MaxInArraySize (default 100) in AutoCrud search — prevent LIKE wildcard DoS and large-IN clause DoS |
Reporting vulnerabilities
Please report security issues privately via GitHub Security Advisories on the affected repository (Sharkable, Sharkable.Cache.Redis, or Sharkable.AutoCrud.SqlSugar). Do not open public issues for suspected vulnerabilities.