trappsec.dev GitHub

Ultra Quickstart

Copy-paste one file and trigger a live trap from your terminal in under 2 minutes.

This example is designed for quick copy-paste local testing only. For proper integration, please consult the Getting Started guide.

Copy-paste this into a file to see trappsec in action immediately.

1. Save as app.py

from flask import Flask, request
import trappsec

app = Flask(__name__)
# Mock Database
user_db = {"username": "guest"}

ts = trappsec.Sentry(app, "DemoApp", "Dev")
ts.identify_user(lambda r: {"user": user_db["username"]})

# 1. Decoy Route (Trap)
ts.trap("/admin/config").methods("GET").respond(200, {"debug": True})

# 2. Honey Field (Watch)
ts.watch("/profile").body("is_admin", intent="PrivEsc")

@app.route("/profile", methods=["GET"])
def get_profile():
    # Bait: Reveal 'is_admin' field to encourage tampering
    return {"username": user_db["username"], "is_admin": False}

@app.route("/profile", methods=["POST"])
def update_profile():
    # Regular logic: Update username
    user_db["username"] = request.json.get("username", user_db["username"])
    # 'is_admin' is ignored here, but trappsec sees it!
    return {"status": "updated", "user": user_db}

if __name__ == "__main__":
    app.run(port=5000)

2. Run

pip install flask trappsec
python app.py

3. Attack

# Check Profile (See Bait)
curl http://localhost:5000/profile
# Output: {"username": "guest", "is_admin": false}

# Trigger Watch (Try to become admin)
curl -X POST http://localhost:5000/profile \
     -H "Content-Type: application/json" \
     -d '{"username": "hacker", "is_admin": true}'

# Trigger Trap (Test alerting)
curl http://localhost:5000/admin/config

1. Save as app.js

const express = require('express');
const { Sentry } = require('trappsec');

const app = express();
app.use(express.json());

// Mock Database
let userDb = { username: "guest" };

const ts = new Sentry(app, "DemoApp", "Dev");
ts.identify_user((req) => ({ user: userDb.username }));

// 1. Decoy Route (Trap)
ts.trap("/admin/config").methods("GET")
    .respond({ status: 200, body: { debug: true } });

// 2. Honey Field (Watch)
ts.watch("/profile").body("is_admin", { intent: "PrivEsc" });

app.get("/profile", (req, res) => {
    // Bait: Reveal 'is_admin' field
    res.json({ ...userDb, is_admin: false });
});

app.post("/profile", (req, res) => {
    // Regular logic: Update username
    if (req.body.username) userDb.username = req.body.username;
    // 'is_admin' is ignored here, but trappsec sees it!
    res.json({ status: "updated", user: userDb });
});

app.listen(5000, () => console.log("Running on port 3000"));

2. Run

npm install express trappsec
node app.js

3. Attack

# Check Profile (See Bait)
curl http://localhost:5000/profile
# Output: {"username": "guest", "is_admin": false}

# Trigger Watch (Try to become admin)
curl -X POST http://localhost:5000/profile \
     -H "Content-Type: application/json" \
     -d '{"username": "hacker", "is_admin": true}'

# Trigger Trap (Test alerting)
curl http://localhost:5000/admin/config

1. Save as main.go

package main

import (
    "net/http"
    "sync"

    "github.com/gin-gonic/gin"
    trappsec "github.com/trappsec-dev/trappsec/packages/go/gin"
)

var (
    mu     sync.Mutex
    userDB = map[string]string{"username": "guest"}
)

func main() {
    r := gin.New()
    app := trappsec.InstallSentry(r, "DemoApp", "Dev")

    app.IdentifyUser(func(req any) *trappsec.AuthContext {
        c := req.(*gin.Context)
        if uid := c.GetHeader("x-user-id"); uid != "" {
            return &trappsec.AuthContext{User: uid, Role: "user"}
        }
        return nil
    })

    // 1. Decoy Route (Trap)
    app.Trap("/admin/config").Methods("GET").Respond(trappsec.ResponseConfig{
        Status: 200, Body: map[string]any{"debug": true},
    })

    // 2. Honey Field (Watch)
    app.Watch("/profile").Body("is_admin", trappsec.NoDefault, "PrivEsc")

    app.GET("/profile", func(c *gin.Context) {
        mu.Lock()
        u := userDB["username"]
        mu.Unlock()
        // Bait: Reveal 'is_admin' field to encourage tampering
        c.JSON(http.StatusOK, gin.H{"username": u, "is_admin": false})
    })

    app.POST("/profile", func(c *gin.Context) {
        var body map[string]any
        _ = c.ShouldBindJSON(&body)
        // Regular logic: Update username
        if u, ok := body["username"].(string); ok {
            mu.Lock()
            userDB["username"] = u
            mu.Unlock()
        }
        // 'is_admin' is ignored here, but trappsec sees it!
        mu.Lock()
        u := userDB["username"]
        mu.Unlock()
        c.JSON(http.StatusOK, gin.H{
            "status": "updated",
            "user":   gin.H{"username": u},
        })
    })

    app.Run(":5000")
}

2. Run

go mod init demo
go mod tidy
go run main.go

3. Attack

# Check Profile (See Bait)
curl http://localhost:5000/profile
# Output: {"is_admin":false,"username":"guest"}

# Trigger Watch (Try to become admin)
curl -X POST http://localhost:5000/profile \
     -H "Content-Type: application/json" \
     -d '{"username": "hacker", "is_admin": true}'

# Trigger Trap (Test alerting)
curl http://localhost:5000/admin/config


Windows Users: The curl command syntax differs for Windows Command Prompt (cmd.exe). Use double quotes for JSON and escape inner quotes:

curl -X POST http://localhost:5000/profile \
  -H "Content-Type: application/json" \
  -d "{\"username\": \"hacker\", \"is_admin\": true}"