Skip to content

Commit 4079b11

Browse files
authored
feat(templates): implement reusable attack templates (#95) (#128)
## Summary - Add `attack_templates` table with project-scoped CRUD, unique `(projectId, name)` constraint, `hashTypeId` FK, and `tags` text array for categorization - Implement full REST API: list, create, get, update, delete, instantiate (returns unsaved attack payload), and import (clone template into current project) - Add Templates management page with resource name resolution, tags support, and campaign wizard "Start from Template" picker integration - Add 20 backend tests covering schema validation, `extractAttackPayload` logic, auth guards, project-scope enforcement, and API contract Closes #95 ## Test Plan - [ ] Create a template via the Templates page -- verify it appears in the list with correct resource names - [ ] Edit a template -- verify fields update, unique name constraint enforced - [ ] Delete a template -- verify removal from list - [ ] Open campaign wizard > Step 2 > "Start from Template" -- verify attack form pre-fills from selected template - [ ] Verify cross-project template access returns 404 - [ ] Verify viewers can list/view templates but cannot create/edit/delete - [ ] Run `just check` -- build, lint, type-check all pass - [ ] Run `just test-backend` -- 124 tests pass (20 new) - [ ] Run `just test-frontend` -- 119 tests pass --------- Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
1 parent 6c94fc6 commit 4079b11

26 files changed

Lines changed: 6226 additions & 26 deletions

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@ This file provides AI coding assistants with project context. All substantive do
1616
- `.kiro/steering/tech.md` contains explicit constraints on what NOT to introduce. Respect these constraints.
1717
- Prefer mermaid diagrams for architectural or sequence diagrams in documentation.
1818
- Agents (hashcat workers) are the primary API consumer. Never break the agent API to improve the dashboard experience.
19+
20+
## Agent Rules <!-- tessl-managed -->
21+
22+
@.tessl/RULES.md follow the [instructions](.tessl/RULES.md)

AI_POLICY.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# AI Usage Policy
2+
3+
We build operator-focused security tools. AI coding assistants are part of how we do that. This policy is not anti-AI -- it is pro-accountability.
4+
5+
Think of AI assistance like spellcheck. It catches typos, suggests corrections, and speeds up the mechanical parts of writing. But you are still responsible for your words and their consequences.
6+
7+
## The Rule
8+
9+
**You own every line you submit.** You must be able to explain what
10+
it does and how it interacts with the rest of the system without asking your AI to explain it back to you.
11+
12+
Everything else follows from that.
13+
14+
## How We Work
15+
16+
- **Disclose your tools.** Note what you used in your PR description -- Claude Code, Copilot, Cursor, whatever. No specific format required.
17+
18+
- **Review AI-generated text before posting.** Issues, discussions, and PR descriptions must reflect your understanding, not a language model's first draft. Read it, cut the filler, make sure it says what you mean.
19+
20+
- **No AI-generated media.** No generated images, logos, audio, or video. Text-based diagrams (ASCII art, Mermaid) and code are acceptable.
21+
22+
- **Unreviewed output gets closed.** Hallucinated APIs, boilerplate that ignores project conventions, suggestions you clearly did not run -- these get closed without review. We are not a QA service for your AI's output.
23+
24+
## Why
25+
26+
Transparent by design means knowing what the code does and why it is there. Tested under pressure means every change was understood by the person who submitted it. AI makes capable engineers faster. It does not replace the understanding that makes contributions trustworthy.
27+
28+
Every pull request is reviewed by a human. Submitting work you do not understand shifts that burden onto maintainers. That is not how we operate.
29+
30+
## New Contributors
31+
32+
Use AI to learn the codebase. Read the code it generates. Run it. Break it. Then submit work that reflects your understanding. We will help you through review -- that deal only works if the code is yours.

CONTRIBUTING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
- **[Testing](./docs/testing.md)** -- test strategy, patterns, bun:test specifics
88
- **[Known Gotchas](./GOTCHAS.md)** -- hard-won lessons by domain (TypeScript, Hono, Drizzle, etc.)
99

10+
## AI Assistance
11+
12+
We accept considerate AI-assisted contributions. Install `tessl install tessl-labs/good-oss-citizen` first. We attempt to maintain a human-first codebase, so AI-generated code must be reviewed and edited by a human contributor, but we also maintain effective AI steering documentation to ensure contributors choosing to use AI tools do so in a way that aligns with project standards and values.
13+
1014
## Development Setup
1115

1216
### Prerequisites

packages/backend/src/config/env.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const envSchema = z.object({
2424

2525
// BetterAuth (generate with: openssl rand -base64 32)
2626
BETTER_AUTH_SECRET: z.string().min(32),
27+
BETTER_AUTH_URL: z.string().url().optional(),
2728
});
2829

2930
export type Env = z.infer<typeof envSchema>;

packages/backend/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { getQueueManager, setQueueManager } from './queue/context.js';
1515
import { QueueManager } from './queue/manager.js';
1616
import { agentRoutes } from './routes/agent/index.js';
1717
import { dashboardAgentRoutes } from './routes/dashboard/agents.js';
18+
import { attackTemplateRoutes } from './routes/dashboard/attack-templates.js';
1819
import { authRoutes } from './routes/dashboard/auth.js';
1920
import { campaignRoutes } from './routes/dashboard/campaigns.js';
2021
import { createEventRoutes } from './routes/dashboard/events.js';
@@ -83,7 +84,7 @@ app.get('/health', async (c) => {
8384

8485
// ─── BetterAuth Handler ──────────────────────────────────────────────
8586

86-
app.on(['POST', 'GET'], '/api/auth/**', async (c) => {
87+
app.on(['POST', 'GET'], '/api/auth/*', async (c) => {
8788
try {
8889
return await auth.handler(c.req.raw);
8990
} catch (err) {
@@ -102,6 +103,7 @@ app.route('/api/v1/dashboard/projects', projectRoutes);
102103
app.route('/api/v1/dashboard/agents', dashboardAgentRoutes);
103104
app.route('/api/v1/dashboard/resources', resourceRoutes);
104105
app.route('/api/v1/dashboard/hashes', hashRoutes);
106+
app.route('/api/v1/dashboard/attack-templates', attackTemplateRoutes);
105107
app.route('/api/v1/dashboard/campaigns', campaignRoutes);
106108
app.route('/api/v1/dashboard/tasks', taskRoutes);
107109
app.route('/api/v1/dashboard/stats', statsRoutes);

packages/backend/src/lib/auth.ts

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import { db } from '../db/index.js';
66

77
export const auth = betterAuth({
88
basePath: '/api/auth',
9+
...(env.BETTER_AUTH_URL ? { baseURL: env.BETTER_AUTH_URL } : {}),
910
secret: env.BETTER_AUTH_SECRET,
1011

1112
database: drizzleAdapter(db, {
1213
provider: 'pg',
1314
schema: {
14-
user: schema.users,
15+
users: schema.users,
1516
session: schema.baSessions,
16-
account: schema.baAccounts,
17+
ba_accounts: schema.baAccounts,
1718
verification: schema.baVerifications,
1819
},
1920
}),
@@ -42,30 +43,10 @@ export const auth = betterAuth({
4243

4344
user: {
4445
modelName: 'users',
45-
fields: {
46-
name: 'name',
47-
email: 'email',
48-
emailVerified: 'email_verified',
49-
image: 'image',
50-
createdAt: 'created_at',
51-
updatedAt: 'updated_at',
52-
},
5346
},
5447

5548
account: {
5649
modelName: 'ba_accounts',
57-
fields: {
58-
userId: 'user_id',
59-
accountId: 'account_id',
60-
providerId: 'provider_id',
61-
accessToken: 'access_token',
62-
refreshToken: 'refresh_token',
63-
accessTokenExpiresAt: 'access_token_expires_at',
64-
refreshTokenExpiresAt: 'refresh_token_expires_at',
65-
idToken: 'id_token',
66-
createdAt: 'created_at',
67-
updatedAt: 'updated_at',
68-
},
6950
},
7051

7152
advanced: {

0 commit comments

Comments
 (0)