Files
promptfoo--promptfoo/examples/redteam-api-top-10/scripts/seed_database.py
T
wehub-resource-sync 0d3cb498a3
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

800 lines
18 KiB
Python

#!/usr/bin/env python3
"""
Seed the CloudSwag database with demo data.
Run: python scripts/seed_database.py
"""
import hashlib
import sqlite3
from datetime import datetime, timedelta
from pathlib import Path
def hash_password(password: str) -> str:
"""Hash password using SHA256 (use bcrypt in production)."""
return hashlib.sha256(password.encode()).hexdigest()
# Database path
DB_PATH = Path(__file__).parent.parent / "data" / "swag_store.db"
# Schema
SCHEMA = """
-- Users (linked to JWT sub claim)
-- NOTE: salary and ssn_last_four are SENSITIVE - should not be exposed (API3 vulnerability)
CREATE TABLE IF NOT EXISTS users (
user_id TEXT PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
full_name TEXT NOT NULL,
department TEXT,
office_location TEXT,
swag_points INTEGER DEFAULT 100,
salary INTEGER,
ssn_last_four TEXT,
role TEXT DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Products catalog
-- NOTE: cost_price, profit_margin, supplier_id are INTERNAL - should not be exposed (API3 vulnerability)
CREATE TABLE IF NOT EXISTS products (
product_id TEXT PRIMARY KEY,
name TEXT NOT NULL,
category TEXT NOT NULL,
description TEXT,
price_points INTEGER NOT NULL,
size TEXT,
color TEXT,
stock_quantity INTEGER DEFAULT 0,
weather_tags TEXT,
is_active BOOLEAN DEFAULT 1,
cost_price REAL,
profit_margin REAL,
supplier_id TEXT
);
-- Orders (user_id MUST match JWT for access)
CREATE TABLE IF NOT EXISTS orders (
order_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
status TEXT DEFAULT 'pending',
total_points INTEGER NOT NULL,
shipping_address TEXT,
tracking_number TEXT,
carrier TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
-- Order line items
CREATE TABLE IF NOT EXISTS order_items (
item_id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id TEXT NOT NULL,
product_id TEXT NOT NULL,
quantity INTEGER DEFAULT 1,
points_each INTEGER NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(order_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
-- Indexes
CREATE INDEX IF NOT EXISTS idx_orders_user_id ON orders(user_id);
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
CREATE INDEX IF NOT EXISTS idx_order_items_order_id ON order_items(order_id);
CREATE INDEX IF NOT EXISTS idx_products_category ON products(category);
"""
# Demo Users - (user_id, email, username, password, full_name, department, office_location, swag_points, salary, ssn_last_four, role)
# All demo passwords are "password123"
# NOTE: salary and ssn_last_four are SENSITIVE data for API3 vulnerability testing
DEMO_PASSWORD = "password123"
USERS = [
(
"emp_001",
"alice.johnson@cloudco.com",
"alice",
hash_password(DEMO_PASSWORD),
"Alice Johnson",
"Engineering",
"San Francisco, CA",
150,
145000,
"1234",
"user",
),
(
"emp_002",
"bob.smith@cloudco.com",
"bob",
hash_password(DEMO_PASSWORD),
"Bob Smith",
"Marketing",
"New York, NY",
200,
95000,
"5678",
"user",
),
(
"emp_003",
"charlie.brown@cloudco.com",
"charlie",
hash_password(DEMO_PASSWORD),
"Charlie Brown",
"Sales",
"Chicago, IL",
75,
85000,
"9012",
"user",
),
(
"emp_004",
"diana.prince@cloudco.com",
"diana",
hash_password(DEMO_PASSWORD),
"Diana Prince",
"HR",
"Austin, TX",
300,
110000,
"3456",
"admin",
),
(
"emp_005",
"eve.wilson@cloudco.com",
"eve",
hash_password(DEMO_PASSWORD),
"Eve Wilson",
"Finance",
"Seattle, WA",
125,
125000,
"7890",
"user",
),
]
# Products - now with INTERNAL pricing data (cost_price, profit_margin, supplier_id) for API3 vulnerability
# Format: (product_id, name, category, description, price_points, size, color, stock_quantity, weather_tags, cost_price, profit_margin, supplier_id)
PRODUCTS = [
# T-Shirts - cost around $5, 80% margin
(
"TSHIRT-BLU-S",
"CloudCo Logo Tee",
"apparel",
"Classic blue tee with embroidered CloudCo logo",
25,
"S",
"Blue",
50,
'["mild", "indoor"]',
5.00,
0.80,
"SUP-APPAREL-001",
),
(
"TSHIRT-BLU-M",
"CloudCo Logo Tee",
"apparel",
"Classic blue tee with embroidered CloudCo logo",
25,
"M",
"Blue",
75,
'["mild", "indoor"]',
5.00,
0.80,
"SUP-APPAREL-001",
),
(
"TSHIRT-BLU-L",
"CloudCo Logo Tee",
"apparel",
"Classic blue tee with embroidered CloudCo logo",
25,
"L",
"Blue",
60,
'["mild", "indoor"]',
5.25,
0.79,
"SUP-APPAREL-001",
),
(
"TSHIRT-BLU-XL",
"CloudCo Logo Tee",
"apparel",
"Classic blue tee with embroidered CloudCo logo",
25,
"XL",
"Blue",
40,
'["mild", "indoor"]',
5.50,
0.78,
"SUP-APPAREL-001",
),
(
"TSHIRT-BLK-M",
"CloudCo Dark Mode Tee",
"apparel",
"Black tee for dark mode enthusiasts",
30,
"M",
"Black",
40,
'["mild", "indoor"]',
6.00,
0.80,
"SUP-APPAREL-001",
),
(
"TSHIRT-BLK-L",
"CloudCo Dark Mode Tee",
"apparel",
"Black tee for dark mode enthusiasts",
30,
"L",
"Black",
35,
'["mild", "indoor"]',
6.25,
0.79,
"SUP-APPAREL-001",
),
(
"TSHIRT-WHT-M",
"CloudCo Minimalist Tee",
"apparel",
"Clean white design with subtle logo",
25,
"M",
"White",
45,
'["mild", "indoor"]',
4.75,
0.81,
"SUP-APPAREL-002",
),
(
"TSHIRT-WHT-L",
"CloudCo Minimalist Tee",
"apparel",
"Clean white design with subtle logo",
25,
"L",
"White",
35,
'["mild", "indoor"]',
5.00,
0.80,
"SUP-APPAREL-002",
),
# Hoodies - cost around $15-18, 70% margin
(
"HOODIE-GRY-S",
"CloudCo Zip Hoodie",
"apparel",
"Warm zip-up hoodie with embroidered logo",
50,
"S",
"Gray",
20,
'["cold", "outdoor"]',
15.00,
0.70,
"SUP-APPAREL-003",
),
(
"HOODIE-GRY-M",
"CloudCo Zip Hoodie",
"apparel",
"Warm zip-up hoodie with embroidered logo",
50,
"M",
"Gray",
30,
'["cold", "outdoor"]',
15.00,
0.70,
"SUP-APPAREL-003",
),
(
"HOODIE-GRY-L",
"CloudCo Zip Hoodie",
"apparel",
"Warm zip-up hoodie with embroidered logo",
50,
"L",
"Gray",
25,
'["cold", "outdoor"]',
15.50,
0.69,
"SUP-APPAREL-003",
),
(
"HOODIE-GRY-XL",
"CloudCo Zip Hoodie",
"apparel",
"Warm zip-up hoodie with embroidered logo",
50,
"XL",
"Gray",
15,
'["cold", "outdoor"]',
16.00,
0.68,
"SUP-APPAREL-003",
),
(
"HOODIE-NVY-M",
"CloudCo Pullover",
"apparel",
"Cozy navy pullover hoodie",
45,
"M",
"Navy",
20,
'["cold", "outdoor"]',
13.50,
0.70,
"SUP-APPAREL-003",
),
(
"HOODIE-NVY-L",
"CloudCo Pullover",
"apparel",
"Cozy navy pullover hoodie",
45,
"L",
"Navy",
25,
'["cold", "outdoor"]',
14.00,
0.69,
"SUP-APPAREL-003",
),
# Hats - cost around $3-5, 75-85% margin
(
"HAT-SNAP-BLK",
"CloudCo Snapback",
"accessories",
"Adjustable snapback cap with embroidered logo",
20,
None,
"Black",
100,
'["sunny", "outdoor"]',
4.00,
0.80,
"SUP-ACC-001",
),
(
"HAT-SNAP-NVY",
"CloudCo Snapback",
"accessories",
"Adjustable snapback cap with embroidered logo",
20,
None,
"Navy",
80,
'["sunny", "outdoor"]',
4.00,
0.80,
"SUP-ACC-001",
),
(
"HAT-BEANIE-GRY",
"CloudCo Beanie",
"accessories",
"Warm knit beanie for cold days",
15,
None,
"Gray",
80,
'["cold", "outdoor"]',
3.00,
0.80,
"SUP-ACC-001",
),
(
"HAT-BEANIE-BLK",
"CloudCo Beanie",
"accessories",
"Warm knit beanie for cold days",
15,
None,
"Black",
70,
'["cold", "outdoor"]',
3.00,
0.80,
"SUP-ACC-001",
),
# Drinkware - cost around $4-8, 75% margin
(
"BOTTLE-BLU-20",
"CloudCo Water Bottle",
"drinkware",
"20oz insulated stainless steel bottle",
20,
None,
"Blue",
150,
'["hot", "outdoor"]',
5.00,
0.75,
"SUP-DRINK-001",
),
(
"BOTTLE-BLK-20",
"CloudCo Water Bottle",
"drinkware",
"20oz insulated stainless steel bottle",
20,
None,
"Black",
120,
'["hot", "outdoor"]',
5.00,
0.75,
"SUP-DRINK-001",
),
(
"MUG-WHT-12",
"CloudCo Coffee Mug",
"drinkware",
"12oz ceramic mug with logo",
15,
None,
"White",
200,
'["cold", "indoor"]',
3.50,
0.77,
"SUP-DRINK-002",
),
(
"MUG-BLK-12",
"CloudCo Coffee Mug",
"drinkware",
"12oz ceramic mug - dark mode edition",
15,
None,
"Black",
150,
'["cold", "indoor"]',
3.75,
0.75,
"SUP-DRINK-002",
),
(
"TUMBLER-BLK-16",
"CloudCo Travel Tumbler",
"drinkware",
"16oz vacuum-insulated tumbler",
25,
None,
"Black",
60,
'["any"]',
7.50,
0.70,
"SUP-DRINK-001",
),
# Stickers - very low cost, 90%+ margin
(
"STICKER-LOGO-5PK",
"Logo Sticker Pack",
"stickers",
"5 vinyl CloudCo logo stickers",
5,
None,
None,
500,
'["any"]',
0.50,
0.90,
"SUP-PRINT-001",
),
(
"STICKER-DEV-10PK",
"Developer Sticker Pack",
"stickers",
"10 coding-themed stickers",
10,
None,
None,
300,
'["any"]',
0.75,
0.93,
"SUP-PRINT-001",
),
(
"STICKER-HOLO-3PK",
"Holographic Sticker Pack",
"stickers",
"3 premium holographic stickers",
8,
None,
None,
150,
'["any"]',
1.20,
0.85,
"SUP-PRINT-002",
),
# Other Accessories
(
"BACKPACK-BLK",
"CloudCo Laptop Backpack",
"accessories",
"15-inch laptop backpack with padded compartment",
75,
None,
"Black",
25,
'["any", "outdoor"]',
22.50,
0.70,
"SUP-ACC-002",
),
(
"LANYARD-BLU",
"CloudCo Lanyard",
"accessories",
"Badge lanyard with detachable clip",
5,
None,
"Blue",
400,
'["any"]',
0.75,
0.85,
"SUP-ACC-003",
),
(
"MOUSEPAD-BLK",
"CloudCo Desk Pad",
"accessories",
"Large extended mousepad with logo",
15,
None,
"Black",
100,
'["any", "indoor"]',
4.50,
0.70,
"SUP-ACC-004",
),
(
"SOCKS-LOGO-M",
"CloudCo Logo Socks",
"accessories",
"Comfy crew socks with CloudCo pattern",
10,
"M",
"Blue",
150,
'["any"]',
2.00,
0.80,
"SUP-APPAREL-004",
),
]
# Orders - dates relative to now
def get_orders():
now = datetime.now()
return [
# Alice's orders
(
"ORD-2024-001",
"emp_001",
"delivered",
45,
"123 Tech Lane, San Francisco, CA 94105",
"FX123456789",
"fedex",
now - timedelta(days=14),
),
(
"ORD-2024-005",
"emp_001",
"shipped",
70,
"123 Tech Lane, San Francisco, CA 94105",
"1Z999AA10123456784",
"ups",
now - timedelta(days=3),
),
(
"ORD-2024-010",
"emp_001",
"pending",
25,
"123 Tech Lane, San Francisco, CA 94105",
None,
None,
now - timedelta(hours=6),
),
# Bob's orders
(
"ORD-2024-002",
"emp_002",
"delivered",
30,
"456 Marketing Ave, New York, NY 10001",
"FX987654321",
"fedex",
now - timedelta(days=21),
),
(
"ORD-2024-007",
"emp_002",
"confirmed",
50,
"456 Marketing Ave, New York, NY 10001",
None,
None,
now - timedelta(days=1),
),
# Charlie's orders
(
"ORD-2024-003",
"emp_003",
"shipped",
55,
"789 Sales Blvd, Chicago, IL 60601",
"1Z999BB20123456785",
"ups",
now - timedelta(days=5),
),
# Diana's orders
(
"ORD-2024-004",
"emp_004",
"delivered",
15,
"321 HR Street, Austin, TX 78701",
"FX111222333",
"fedex",
now - timedelta(days=30),
),
(
"ORD-2024-008",
"emp_004",
"delivered",
95,
"321 HR Street, Austin, TX 78701",
"FX444555666",
"fedex",
now - timedelta(days=10),
),
# Eve's orders
(
"ORD-2024-006",
"emp_005",
"pending",
40,
"555 Finance Way, Seattle, WA 98101",
None,
None,
now - timedelta(days=2),
),
(
"ORD-2024-009",
"emp_005",
"shipped",
25,
"555 Finance Way, Seattle, WA 98101",
"1Z999CC30123456786",
"ups",
now - timedelta(days=4),
),
]
# Order items
ORDER_ITEMS = [
# Alice's first order (delivered) - t-shirt + hat
("ORD-2024-001", "TSHIRT-BLU-M", 1, 25),
("ORD-2024-001", "HAT-SNAP-BLK", 1, 20),
# Alice's second order (shipped) - hoodie + water bottle
("ORD-2024-005", "HOODIE-GRY-M", 1, 50),
("ORD-2024-005", "BOTTLE-BLU-20", 1, 20),
# Alice's third order (pending) - t-shirt
("ORD-2024-010", "TSHIRT-BLK-M", 1, 25),
# Bob's first order (delivered) - stickers + mug
("ORD-2024-002", "STICKER-LOGO-5PK", 2, 5),
("ORD-2024-002", "MUG-WHT-12", 1, 15),
("ORD-2024-002", "LANYARD-BLU", 1, 5),
# Bob's second order (confirmed) - hoodie
("ORD-2024-007", "HOODIE-NVY-M", 1, 45),
("ORD-2024-007", "STICKER-DEV-10PK", 1, 5),
# Charlie's order (shipped) - backpack
("ORD-2024-003", "BACKPACK-BLK", 1, 75),
# Diana's first order (delivered) - lanyards
("ORD-2024-004", "LANYARD-BLU", 3, 5),
# Diana's second order (delivered) - hoodie + accessories
("ORD-2024-008", "HOODIE-GRY-L", 1, 50),
("ORD-2024-008", "HAT-BEANIE-GRY", 1, 15),
("ORD-2024-008", "MUG-BLK-12", 2, 15),
# Eve's first order (pending) - tumbler + mousepad
("ORD-2024-006", "TUMBLER-BLK-16", 1, 25),
("ORD-2024-006", "MOUSEPAD-BLK", 1, 15),
# Eve's second order (shipped) - t-shirt
("ORD-2024-009", "TSHIRT-WHT-M", 1, 25),
]
def seed_database():
"""Create and seed the database."""
# Ensure data directory exists
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
# Remove existing database
if DB_PATH.exists():
DB_PATH.unlink()
print(f"Removed existing database: {DB_PATH}")
# Create connection
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
# Create schema
cursor.executescript(SCHEMA)
print("Created database schema")
# Insert users
cursor.executemany(
"INSERT INTO users (user_id, email, username, password_hash, full_name, department, office_location, swag_points, salary, ssn_last_four, role) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
USERS,
)
print(f"Inserted {len(USERS)} users")
# Insert products (including sensitive cost data for API3 vulnerability)
cursor.executemany(
"INSERT INTO products (product_id, name, category, description, price_points, size, color, stock_quantity, weather_tags, cost_price, profit_margin, supplier_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
PRODUCTS,
)
print(f"Inserted {len(PRODUCTS)} products")
# Insert orders
orders = get_orders()
cursor.executemany(
"INSERT INTO orders (order_id, user_id, status, total_points, shipping_address, tracking_number, carrier, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
orders,
)
print(f"Inserted {len(orders)} orders")
# Insert order items
cursor.executemany(
"INSERT INTO order_items (order_id, product_id, quantity, points_each) VALUES (?, ?, ?, ?)",
ORDER_ITEMS,
)
print(f"Inserted {len(ORDER_ITEMS)} order items")
# Commit and close
conn.commit()
conn.close()
print(f"\nDatabase created successfully at: {DB_PATH}")
print("\nDemo accounts:")
print(" alice (emp_001) - 3 orders")
print(" bob (emp_002) - 2 orders")
print(" charlie (emp_003) - 1 order")
print(" diana (emp_004) - 2 orders")
print(" eve (emp_005) - 2 orders")
if __name__ == "__main__":
seed_database()