# GB5Framework — Consolidated Security Analysis

**Date:** 2026-03-02
**Scope:** All layers — Framework, Shared, Solution modules
**Severity Key:** 🔴 CRITICAL | 🟠 HIGH | 🟡 MEDIUM | 🟢 LOW

---

## OWASP Top 10 Coverage

| OWASP Category | Found in GB5 | Severity |
|----------------|-------------|----------|
| A01 Broken Access Control | AllowAnonymous on all endpoints | 🔴 CRITICAL |
| A02 Cryptographic Failures | Hardcoded AES key + static IV | 🔴 CRITICAL |
| A03 Injection | SQL injection via string interpolation | 🔴 CRITICAL |
| A05 Security Misconfiguration | CORS wildcard, AllowedHosts wildcard, SSL bypass | 🔴 CRITICAL |
| A06 Vulnerable Components | Dual PDF libs, PuppeteerSharp in shared | 🟡 MEDIUM |
| A07 Auth & Session Failures | Hardcoded admin credentials | 🔴 CRITICAL |
| A08 Software & Data Integrity | OutBox transaction bug enables phantom events | 🟠 HIGH |
| A09 Logging & Monitoring | No logging in DAL/BLL, exceptions swallowed | 🟠 HIGH |
| A10 SSRF | Not analyzed (Keycloak calls could be SSRF if realm is user-provided) | 🟡 MEDIUM |

---

## SEC-01: SSL/TLS Validation Completely Disabled

**Severity:** 🔴 CRITICAL
**Files:**
- `GB5Framework/FrameworkSL/Program.cs:200–205`
- `GB5Framework/FrameworkSL/Controllers/KeyCloak/KeyCloakService.cs:150, 274, 505, 545, 581, 625`

**Code:**
```csharp
ServerCertificateCustomValidationCallback =
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
```

**Risk:** Man-in-the-Middle attack can intercept:
- All OAuth tokens exchanged with Keycloak
- User credentials during login flows
- Admin operations (user creation, password reset, role assignment)

**Fix:**
```csharp
// Remove from production code.
// If self-signed certs needed in dev only:
if (env.IsDevelopment())
{
    handler.ServerCertificateCustomValidationCallback =
        HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
}
// In production, install proper certificates or use Let's Encrypt
```

---

## SEC-02: Hardcoded AES Encryption Key with Static IV

**Severity:** 🔴 CRITICAL
**File:** `GB5Framework/FrameworkSL/Controllers/KeyCloak/KeyCloakService.cs:187, 402`

**Code:**
```csharp
string encrypted = EncryptString(combined, "12345678901234567890123456789012");
aes.IV = new byte[16]; // All zeros — deterministic encryption
```

**Risks:**
- Key in source = permanently compromised if repo is ever leaked/cloned
- Static IV = same plaintext produces same ciphertext → dictionary attacks, pattern analysis
- 32-char ASCII key = effectively 128-bit entropy (less than a proper random 256-bit key)

**Fix:**
```csharp
// 1. Generate random key: openssl rand -base64 32
// 2. Store in appsettings/vault: "Security:TokenEncryptionKey": "<base64>"
// 3. In code:
private readonly byte[] _key;
public KeyCloakService(IConfiguration config)
{
    var keyBase64 = config["Security:TokenEncryptionKey"]
        ?? throw new InvalidOperationException("Encryption key not configured");
    _key = Convert.FromBase64String(keyBase64);
}

// Always use random IV prepended to ciphertext:
using var aes = Aes.Create();
aes.Key = _key;
aes.GenerateIV(); // Random 16 bytes
// Ciphertext = IV (16 bytes) + encrypted data
```

---

## SEC-03: Hardcoded Keycloak Admin Credentials

**Severity:** 🔴 CRITICAL
**File:** `GB5Framework/FrameworkSL/Controllers/KeyCloak/KeyCloakService.cs:512–514`

**Code:**
```csharp
new KeyValuePair<string, string>("username", "admin"),
new KeyValuePair<string, string>("password", "admin"),
```

**Risk:** Admin credentials for identity provider are in source code. Attacker can:
- Create backdoor admin accounts
- Reset any user's password
- Modify roles and permissions
- Disable MFA
- Export all user PII

**Fix:**
```csharp
// Environment variable injection (preferred for containers):
// In docker-compose.yml or k8s secret:
// KEYCLOAK__ADMINUSERNAME=admin
// KEYCLOAK__ADMINPASSWORD=<strong-password>

// In code:
_adminUsername = config["Keycloak:AdminUsername"]
    ?? throw new InvalidOperationException("Keycloak admin username not configured");
_adminPassword = config["Keycloak:AdminPassword"]
    ?? throw new InvalidOperationException("Keycloak admin password not configured");
```

---

## SEC-04: Hardcoded Database Password

**Severity:** 🔴 CRITICAL
**File:** `GB5Shared/Connection/ApplicationConnection.cs:291, 604`

**Code:**
```csharp
"Password=devuser@123"
"Password=" + "devuser@123"
```

**Risk:** Direct database access bypassing all application-level security, audit logging, and rate limiting.

**Fix:**
```csharp
// .NET 9 User Secrets for development:
// dotnet user-secrets set "Database:Password" "actual_password"

// Production: environment variable or secret manager
var password = config["Database:Password"]
    ?? Environment.GetEnvironmentVariable("DB_PASSWORD")
    ?? throw new InvalidOperationException("Database password not configured");
```

---

## SEC-05: CORS Wildcard with Credentials

**Severity:** 🔴 CRITICAL
**Files:** ALL 18 module `Program.cs` files + `GB5Framework/FrameworkSL/Program.cs`

**Code:**
```csharp
.SetIsOriginAllowed(_ => true)  // Allows all origins
.AllowCredentials()              // With session cookies/tokens
```

**Risk:** Any website (malicious) can make requests to GB5 APIs on behalf of authenticated users (CSRF). Combined with open endpoints (SEC-06), this allows full application compromise from any web page.

**Note:** The combination of `SetIsOriginAllowed(_ => true)` AND `AllowCredentials()` is explicitly warned against in ASP.NET Core documentation as a known CSRF vector.

**Fix:**
```csharp
// appsettings.json
"Cors": { "AllowedOrigins": ["https://gb5.company.com"] }

// Program.cs
var origins = config.GetSection("Cors:AllowedOrigins").Get<string[]>() ?? [];
policy.WithOrigins(origins).AllowAnyHeader().AllowCredentials();
```

---

## SEC-06: AllowAnonymous on All API Endpoints

**Severity:** 🔴 CRITICAL
**Files:** All FastEndpoint files across all 18 modules (100+ files)

**Code:**
```csharp
public override void Configure()
{
    Get("/SomeEntity/GetSomeEntity");
    AllowAnonymous(); // No authentication
}
```

**Risk:** Complete bypass of all authentication. Any caller (local network, internet if exposed) can:
- Read financial data, employee salaries, customer records
- Write transactions, create users, modify configurations
- No audit trail (anonymous = no user context)

**Note:** The Keycloak SSO integration is in place but never enforced at the endpoint level.

**Fix:** Remove `AllowAnonymous()` from all endpoints. FastEndpoints enforces authentication by default when JWT middleware is configured. Add role-based access control:

```csharp
public override void Configure()
{
    Get("/Payelement/GetPayelement");
    // Don't add AllowAnonymous — authentication is default
    Roles("HRManager", "PayrollAdmin");  // Add RBAC
    // Or for claims-based:
    Claims("department", "HR");
}
```

**Bulk fix script approach:** Search all endpoint Configure() methods for `AllowAnonymous()` and remove.

---

## SEC-07: SQL Injection via Table Name String Interpolation

**Severity:** 🔴 CRITICAL
**File:** `GB5Shared/WorkFlow/WorkFlowEngine/WorkFlowEngine.cs:682, 687`

**Code:**
```csharp
string sql = $"SELECT COUNT(1) FROM {tableName} WHERE STATUS = @Status";
string sql2 = $"UPDATE {tableName} SET STATUS = 1 WHERE ID = @Id";
```

**Risk:** If `tableName` is derived from user input (workflow configuration stored in DB), an attacker who can modify workflow configuration can inject SQL:
```
tableName = "Accounts; DROP TABLE Employees;--"
→ SELECT COUNT(1) FROM Accounts; DROP TABLE Employees;-- WHERE STATUS = @Status
```

**Fix:**
```csharp
// Validate against schema whitelist
private static readonly IReadOnlySet<string> _allowedTables = new HashSet<string>(
    StringComparer.OrdinalIgnoreCase)
{
    "WorkflowTasks", "WorkflowInstances", "WorkflowApprovals", "BIZTransactions"
    // ... enumerate all legitimate workflow tables
};

private static string ValidateTableName(string tableName)
{
    if (!_allowedTables.Contains(tableName))
        throw new ArgumentException($"Table '{tableName}' is not permitted in workflow operations");
    return tableName; // Safe to interpolate after whitelist validation
}
```

---

## SEC-08: State Parameter Array Access Without Validation

**Severity:** 🟠 HIGH (Bug + Security)
**File:** `GB5Framework/FrameworkSL/Controllers/KeyCloak/KeyCloakService.cs:139–144`

**Code:**
```csharp
var parts = state.Split('|');
string realm = parts[0];      // IndexOutOfRangeException if malformed
string clientId = parts[1];
string clientSecret = parts[2];
string backendCallback = parts[3];
string feCallback = parts[4];
```

**Risk:**
- Crash the authentication endpoint with a malformed callback URL (DoS on login)
- `clientSecret` extracted from state — should not be stored in URL-exposed state parameter

**Fix:**
```csharp
if (string.IsNullOrWhiteSpace(state))
    return BadRequest("Missing OAuth state parameter");

var parts = state.Split('|');
if (parts.Length < 5)
    return BadRequest("Invalid OAuth state format");

// Validate each part
string realm = Uri.UnescapeDataString(parts[0]);
if (!IsValidRealm(realm))
    return BadRequest("Invalid realm");
```

---

## SEC-09: AllowedHosts Wildcard

**Severity:** 🟡 MEDIUM
**File:** `GB5Framework/FrameworkSL/appsettings.json:8`

**Code:**
```json
"AllowedHosts": "*"
```

**Risk:** HTTP Host header injection. Affects password reset emails (can make links point to attacker domain), server-side URL generation.

**Fix:**
```json
"AllowedHosts": "gb5.company.com;api.gb5.company.com"
```

---

## SEC-10: Full LoginDTO Serialized into Event Messages

**Severity:** 🟡 MEDIUM
**File:** `GB5Shared/EventLogPublish/EventLogPublish.cs:66, 88, 136`

**Code:**
```csharp
// LoginDTO likely contains connection strings, JWT tokens, session keys
await _daprClient.PublishEventAsync(pubSubName, topicName,
    new { Login = loginDTO, Data = data });
```

**Risk:** Sensitive data (DB connection strings, JWT tokens, session data) stored in:
- RabbitMQ message queues (persistent, potentially unencrypted)
- Dapr state store
- Message broker logs
- Zipkin/Jaeger traces (if full payload is captured)

**Fix:**
```csharp
// Create a minimal event context DTO
public record EventContext(int UserId, int RoleId, string TenantId, string CorrelationId);

await _daprClient.PublishEventAsync(pubSubName, topicName,
    new { Context = new EventContext(login.UserId, login.RoleId, login.TenantId, correlationId),
          Data = data });
```

---

## Security Remediation Checklist

### Phase 0 — Emergency (Do Before Any External Access)

- [ ] **SEC-01:** Remove `DangerousAcceptAnyServerCertificateValidator` from all HttpClientHandlers
- [ ] **SEC-02:** Move AES key to configuration, implement random IV
- [ ] **SEC-03:** Move Keycloak admin credentials to environment variables
- [ ] **SEC-04:** Move database password to environment variables / secret manager
- [ ] **SEC-05:** Restrict CORS to specific allowed origins
- [ ] **SEC-06:** Remove `AllowAnonymous()` from all endpoints, add role-based access
- [ ] **SEC-07:** Add table name whitelist validation in WorkFlowEngine

### Phase 1 — Hardening (Sprint 1)

- [ ] **SEC-08:** Add bounds check on state parameter split
- [ ] **SEC-09:** Set AllowedHosts to specific domain names
- [ ] **SEC-10:** Create EventContext DTO to replace LoginDTO in messages

### Ongoing

- [ ] Set up secret scanning in GitLab CI (detect future credential commits)
- [ ] Add rate limiting to authentication endpoints
- [ ] Implement anti-forgery token validation for state-changing operations
- [ ] Schedule quarterly security review

---

## Recommended Secret Management Architecture

```
┌──────────────────────────────────────────────────────────────┐
│                    Secrets Sources                           │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │ HashiCorp    │  │ Azure KeyVault│  │ Env Variables│      │
│  │ Vault        │  │  (if Azure)   │  │ (.env / k8s) │      │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘      │
└─────────┼──────────────────┼──────────────────┼─────────────┘
          └──────────────────┼──────────────────┘
                             ▼
                  IConfiguration (ASP.NET Core)
                             ▼
             Injected via constructor injection
             Never hardcoded, never committed
```

**For .NET 9 implementation:**
```csharp
// Program.cs — load secrets from environment variables
builder.Configuration
    .AddEnvironmentVariables()
    .AddUserSecrets<Program>(optional: true); // Dev only

// Services consume via IConfiguration injection
// Never reference config directly in business logic — use Options<T> pattern
```
