Add hostile fixture setup for authz testing
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"alice_email": "alice.m015@example.com",
|
||||||
|
"bob_email": "bob.m015@example.com",
|
||||||
|
"company_id": 1,
|
||||||
|
"job_id": 1,
|
||||||
|
"correspondence_id": 1,
|
||||||
|
"attachment_id": 1,
|
||||||
|
"api_base": "http://localhost:5202/api"
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# M015 Hostile Fixture Setup
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Produce a trustworthy local runtime for cross-user authorization probes.
|
||||||
|
|
||||||
|
## Key discovery
|
||||||
|
|
||||||
|
The default development SQLite database in `JobTrackerApi/jobtracker.db` is **not** a trustworthy authorization-test target:
|
||||||
|
|
||||||
|
- it contains Identity and some later feature tables
|
||||||
|
- it does **not** contain the core domain tables needed for real cross-user job/correspondence/attachment probing
|
||||||
|
- current startup `Migrate()` behavior is therefore insufficient as the only hostile-test setup path
|
||||||
|
|
||||||
|
## Chosen fixture strategy
|
||||||
|
|
||||||
|
Use a dedicated clean SQLite fixture database created from the current EF model with `EnsureCreated()` semantics through a tiny helper program:
|
||||||
|
|
||||||
|
- helper project: `tools/hostile-fixture-db/`
|
||||||
|
- bootstrap script: `scripts/m015-hostile-fixture.sh`
|
||||||
|
|
||||||
|
This keeps the hostile runtime inside repo code and the real API host while avoiding ad-hoc manual SQL.
|
||||||
|
|
||||||
|
## What the helper does
|
||||||
|
|
||||||
|
- creates a clean `jobtracker.db` under a caller-provided data root
|
||||||
|
- builds the schema from the current `JobTrackerContext` model
|
||||||
|
- verifies the presence of core tables needed for M015:
|
||||||
|
- `Companies`
|
||||||
|
- `JobApplications`
|
||||||
|
- `Correspondences`
|
||||||
|
- `Attachments`
|
||||||
|
- `RuleSettings`
|
||||||
|
- `AspNetUsers`
|
||||||
|
|
||||||
|
## Runtime plan for S02
|
||||||
|
|
||||||
|
1. Run `scripts/m015-hostile-fixture.sh`.
|
||||||
|
2. Start the API with `Data__Root` pointing at that clean fixture root.
|
||||||
|
3. Mint an admin dev token against the fixture DB.
|
||||||
|
4. Create/reuse Alice and Bob through real API paths.
|
||||||
|
5. Seed Alice-owned company/job/correspondence/attachment fixtures through the real API.
|
||||||
|
6. Capture ids for cross-user hostile probes.
|
||||||
|
|
||||||
|
## Honest boundary
|
||||||
|
|
||||||
|
This slice establishes the trusted runtime path and fixture strategy. The full two-user seeded dataset and exploit execution belong in the next slice.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
FIXTURE_ROOT="${FIXTURE_ROOT:-$REPO_ROOT/.tmp/m015-fixture}"
|
||||||
|
API_PORT="${API_PORT:-5202}"
|
||||||
|
API_BASE="http://localhost:${API_PORT}/api"
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||||
|
|
||||||
|
mkdir -p "$FIXTURE_ROOT"
|
||||||
|
|
||||||
|
echo "[m015] creating clean hostile-test db under $FIXTURE_ROOT"
|
||||||
|
dotnet run --project "$REPO_ROOT/tools/hostile-fixture-db/HostileFixtureDb.csproj" -- "$FIXTURE_ROOT" > "$TMP_DIR/db.json"
|
||||||
|
python3 - <<'PY' "$TMP_DIR/db.json"
|
||||||
|
import json, sys
|
||||||
|
payload=json.load(open(sys.argv[1]))
|
||||||
|
required={'Companies','JobApplications','Correspondences','Attachments','RuleSettings','AspNetUsers'}
|
||||||
|
missing=sorted(required-set(payload['tables']))
|
||||||
|
if missing:
|
||||||
|
raise SystemExit(f"missing tables after fixture db init: {', '.join(missing)}")
|
||||||
|
print('[m015] db tables ok')
|
||||||
|
PY
|
||||||
|
|
||||||
|
echo "[m015] ready to start API with:"
|
||||||
|
echo " Data__Root=$FIXTURE_ROOT ASPNETCORE_ENVIRONMENT=Development dotnet run --project JobTrackerApi/JobTrackerApi.csproj"
|
||||||
|
echo "[m015] API base: $API_BASE"
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../JobTrackerBackend/JobTrackerBackend.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using JobTrackerApi.Data;
|
||||||
|
using JobTrackerApi.Services;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
if (args.Length < 1)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("Usage: dotnet run --project tools/hostile-fixture-db -- <data-root>");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataRoot = Path.GetFullPath(args[0]);
|
||||||
|
Directory.CreateDirectory(dataRoot);
|
||||||
|
var dbPath = Path.Combine(dataRoot, "jobtracker.db");
|
||||||
|
if (File.Exists(dbPath))
|
||||||
|
{
|
||||||
|
File.Delete(dbPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
["Data:Root"] = dataRoot,
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var currentUser = new StaticCurrentUserService(null);
|
||||||
|
var options = new DbContextOptionsBuilder<JobTrackerContext>()
|
||||||
|
.UseSqlite($"Data Source={dbPath}")
|
||||||
|
.Options;
|
||||||
|
|
||||||
|
await using var db = new JobTrackerContext(options, currentUser);
|
||||||
|
await db.Database.EnsureDeletedAsync();
|
||||||
|
await db.Database.EnsureCreatedAsync();
|
||||||
|
|
||||||
|
var tables = await db.Database.SqlQueryRaw<string>("SELECT name AS Value FROM sqlite_master WHERE type='table' ORDER BY name;").ToListAsync();
|
||||||
|
var payload = new
|
||||||
|
{
|
||||||
|
dataRoot,
|
||||||
|
dbPath,
|
||||||
|
tables,
|
||||||
|
};
|
||||||
|
Console.WriteLine(JsonSerializer.Serialize(payload));
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
file sealed class StaticCurrentUserService(string? userId) : ICurrentUserService
|
||||||
|
{
|
||||||
|
public string? UserId => userId;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user