diff --git a/docs/security-assessments/M015-hostile-fixture-setup.json b/docs/security-assessments/M015-hostile-fixture-setup.json
new file mode 100644
index 0000000..00efb0b
--- /dev/null
+++ b/docs/security-assessments/M015-hostile-fixture-setup.json
@@ -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"
+}
\ No newline at end of file
diff --git a/docs/security-assessments/M015-hostile-fixture-setup.md b/docs/security-assessments/M015-hostile-fixture-setup.md
new file mode 100644
index 0000000..cd83ff1
--- /dev/null
+++ b/docs/security-assessments/M015-hostile-fixture-setup.md
@@ -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.
diff --git a/scripts/m015-hostile-fixture.sh b/scripts/m015-hostile-fixture.sh
new file mode 100644
index 0000000..3deab70
--- /dev/null
+++ b/scripts/m015-hostile-fixture.sh
@@ -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"
diff --git a/tools/hostile-fixture-db/HostileFixtureDb.csproj b/tools/hostile-fixture-db/HostileFixtureDb.csproj
new file mode 100644
index 0000000..ed196e7
--- /dev/null
+++ b/tools/hostile-fixture-db/HostileFixtureDb.csproj
@@ -0,0 +1,11 @@
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
+
+
diff --git a/tools/hostile-fixture-db/Program.cs b/tools/hostile-fixture-db/Program.cs
new file mode 100644
index 0000000..2087fda
--- /dev/null
+++ b/tools/hostile-fixture-db/Program.cs
@@ -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 -- ");
+ 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
+ {
+ ["Data:Root"] = dataRoot,
+ })
+ .Build();
+
+var currentUser = new StaticCurrentUserService(null);
+var options = new DbContextOptionsBuilder()
+ .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("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;
+}