Skip to content

OWASP-BLT/BLT-Lettuce

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

578 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

File Naming Convention

Welcome message files in the data/ directory use the following naming pattern:

message-<team_id>-<channel_id>-ephemeral.md

  • <team_id>: The Slack team (workspace) ID (e.g., T070JPE5BQQ)
  • <channel_id>: The Slack channel ID (e.g., C06V9S85YR1)

Examples:

  • message-T070JPE5BQQ-C06V9S85YR1-ephemeral.md β€” Ephemeral join message for team T070JPE5BQQ in channel C06V9S85YR1
  • message-T070JPE5BQQ-C06V9S85YR1.md β€” Persistent/channel template fallback for the same team/channel

Backward compatibility:

  • ephemeral-T070JPE5BQQ-C06V9S85YR1.md is still recognized by the worker.

This convention makes it easy to distinguish between ephemeral and persistent messages and to target specific workspaces and channels.

πŸ₯¬ BLT-Lettuce

An intelligent Slack bot for the OWASP community

License GitHub Stars GitHub Forks GitHub Issues GitHub Pull Requests

Contributors Last Commit Commit Activity Repo Size

Python Cloudflare Workers Slack

Pre-commit Code Style: Ruff Conventional Commits

πŸ’¬ Join OWASP Slack Β· πŸ› Report Bug Β· ✨ Request Feature


πŸ“– About

BLT-Lettuce is an intelligent Slack bot designed for the OWASP Slack workspace. It welcomes new members, helps them discover projects, and connects the global security community.

Note: This Slack bot functionality has been incorporated into the main BLT repository and is being transferred back to this repo for better organization.

🎯 Core Features

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        OWASP Slack Workspace                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                  β”‚
                                  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Cloudflare Worker (Python)                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚   Webhook    β”‚  β”‚    Stats     β”‚  β”‚   Project Discovery  β”‚  β”‚
β”‚  β”‚   Handler    β”‚  β”‚   Tracking   β”‚  β”‚      Flowchart       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
          β–Ό                       β–Ό                       β–Ό
β”‚   Cloudflare    β”‚    β”‚    GitHub API   β”‚    β”‚   GitHub Pages  β”‚
β”‚   KV Storage    β”‚    β”‚  (Org scanning) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”„ Project Discovery Flow

The bot uses a conversational flowchart to help users find OWASP projects:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        User Initiates Chat          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  "What type of project interests    β”‚
β”‚   you?" (Multiple Choice)           β”‚
β”‚  β€’ Documentation/Standards          β”‚
β”‚  β€’ Security Tools                   β”‚
β”‚  β€’ Deliberately Insecure Apps       β”‚
β”‚  β€’ Research/Education               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  "What technology stack?"           β”‚
β”‚  β€’ Python  β€’ Java  β€’ JavaScript     β”‚
β”‚  β€’ Go      β€’ .NET  β€’ Any            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Query cached project metadata      β”‚
β”‚  from configured GitHub orgs        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Return matching project links      β”‚
β”‚  with descriptions and stats        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  No matches? Offer to:              β”‚
β”‚  β€’ Start over with different params β”‚
β”‚  β€’ Learn how to start a new project β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“Š Live Stats

View real-time statistics at our Stats Page:

  • πŸ‘‹ Members welcomed
  • ⚑ Commands executed
  • πŸ™ GitHub project health metrics
  • 🌍 Global availability status

☁️ Cloudflare Worker

BLT-Lettuce is now fully powered by a Cloudflare Python Worker (src/worker.py) that serves as the complete backend:

  • Homepage: Serves the stats at the root URL
  • Slack Events: Handles all webhook events (team joins, messages, mentions)
  • Welcome Messages: Sends personalized welcome messages to new members
  • Message Handling: Detects keywords like "contribute" and provides helpful responses
  • Direct Messages: Responds to user DMs
  • Stats Tracking: Tracks statistics in KV storage with atomic updates
  • Stats API: Provides a JSON endpoint for live statistics
  • Multi-Org Support: Can be installed in any Slack organization

API Endpoints

Endpoint Method Description
/ GET Homepage
/webhook POST Slack webhook for events
/stats GET Returns statistics JSON
/health GET Health check endpoint

Slack App Setup

  1. Go to Slack API and create a new app or use the manifest.yaml
  2. If creating manually:
    • Enable Event Subscriptions and add the webhook URL: https://your-worker.workers.dev/webhook
    • Subscribe to these bot events: team_join, message.channels, message.im, app_mention
    • Enable Interactivity and set Request URL to: https://your-worker.workers.dev/webhook
  3. Install the app to your workspace
  4. Copy the Bot User OAuth Token and use it for SLACK_TOKEN
  5. Copy the Signing Secret and use it for SIGNING_SECRET

Required Bot Permissions

  • chat:write - Send messages
  • im:write - Open DM conversations
  • im:read - Read direct messages
  • im:history - Read DM history
  • channels:history - Read channel messages
  • channels:read - View basic channel information
  • users:read - Read user information
  • team:read - Read workspace information

Environment Variables

Required

  • SLACK_TOKEN - Bot User OAuth Token (xoxb-...)
  • SIGNING_SECRET - Slack App Signing Secret

Optional Channel Configuration

For multi-organization deployments, you can configure custom channel IDs:

  • JOINS_CHANNEL_ID - Channel ID where join notifications are posted (optional)
  • CONTRIBUTE_ID - Channel ID for contribution guidelines link (optional)
  • DEPLOYS_CHANNEL - Channel name for deployment notifications (optional)

Note: If these are not set, the bot will skip channel-specific features (like posting join notifications to a monitoring channel) but all core functionality (welcome DMs, keyword detection) will still work.

To find a channel ID in Slack:

  1. Right-click on the channel name β†’ "Copy link"
  2. The ID is the last part: https://workspace.slack.com/archives/C06RMMRMGHE

Stats

Stats are stored in Cloudflare KV and include:

  • joins: Number of new team members who have joined
  • commands: Number of commands run
  • last_updated: Timestamp of last update

Stats are workspace-specific and use optimistic locking to handle concurrent updates.

Security

  • All Slack requests are verified using HMAC signature validation
  • Replay attacks are prevented with timestamp checking (5-minute window)
  • The bot ignores its own messages to prevent loops
  • Error messages are sanitized to avoid exposing internal details

πŸš€ Quick Start

Prerequisites

  • Wrangler CLI for Cloudflare Workers
  • Cloudflare account
  • Slack Bot Token and Signing Secret

Deploy to Cloudflare Workers

  1. Clone the repository

    git clone https://github.com/OWASP-BLT/BLT-Lettuce.git
    cd BLT-Lettuce
  2. Install Wrangler and login

    npm install -g wrangler
    wrangler login
  3. Set up secrets

    wrangler secret put SLACK_TOKEN       # Your Bot User OAuth Token
    wrangler secret put SIGNING_SECRET    # Your Signing Secret

    Or use the automated setup script (recommended for production):

    # Copy the example env file
    cp .env.production.example .env.production
    
    # Edit with your production credentials
    nano .env.production
    
    # Run the setup script
    python scripts/setup-env.py

    See Environment Setup Guide for detailed instructions.

  4. Deploy the worker

    wrangler deploy
  5. Configure Slack App

    • Use the manifest.yaml to create or update your Slack app
    • Or manually configure Event Subscriptions URL: https://your-worker.workers.dev/webhook
    • Subscribe to events: team_join, message.channels, message.im, app_mention

Adding to Any Slack Organization

This bot supports org-wide deployment and can be installed in any Slack workspace:

  1. Create your Slack app using the provided manifest.yaml
  2. Deploy the Cloudflare Worker to your account
  3. Enable Org-Wide App Installation in your Slack app settings
  4. Share the installation URL with other organizations

Each organization will have its own isolated statistics and configuration.


πŸ“ Project Structure

BLT-Lettuce/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ worker.py           # Complete Python worker with all bot logic
β”‚   └── lettuce/            # Bot plugins and modules (for reference)
β”œβ”€β”€ wrangler.toml           # Worker configuration
β”œβ”€β”€ manifest.yaml           # Slack App manifest for easy setup
β”œβ”€β”€ docs/
β”‚   └── index.html          # GitHub Pages stats (reference)
β”œβ”€β”€ app.py                  # Legacy Flask application (kept for reference)
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ projects.json       # OWASP project metadata cache
β”‚   └── repos.json          # Repository categorization
β”œβ”€β”€ tests/                  # Test suite
β”œβ”€β”€ pyproject.toml          # Python dependencies
└── README.md               # This file

Note: The primary application is now the Cloudflare Worker in src/worker.py. The Flask app (app.py) and related plugins are kept for historical reference and may be removed in a future release. All new development should focus on the Cloudflare Worker implementation.


🀝 How to Contribute

We welcome contributions from everyone! Here's how to get started:

  1. Fork the Repository - Click "Fork" at the top right of this page
  2. Clone Your Fork
    git clone https://github.com/YOUR-USERNAME/BLT-Lettuce.git
  3. Create a Branch
    git checkout -b feature/your-feature-name
  4. Make Changes - Follow our coding standards (enforced by pre-commit hooks)
  5. Test Your Changes
    poetry run pytest
  6. Commit with Conventional Commits
    git commit -m "feat: add new feature"
  7. Push and Open a PR
    git push origin feature/your-feature-name

πŸ“Ί Contributing Video Tutorial

Watch our contribution walkthrough video for a step-by-step guide.


πŸ§‘β€πŸ’» Development

Running Tests

poetry run pytest

Code Formatting

poetry run ruff check --fix .
poetry run ruff format .

Pre-commit Hooks

pip install pre-commit
pre-commit install

πŸš€ Deployment Guide

This section contains the complete deployment guide previously kept in DEPLOYMENT.md.

Prerequisites

Before you begin, make sure you have:

  • A Cloudflare account (free tier works)
  • A Slack workspace where you have admin permissions
  • Node.js and npm installed (for Wrangler CLI)

Step 1: Set Up Cloudflare

1.1 Install Wrangler CLI

npm install -g wrangler

1.2 Login to Cloudflare

wrangler login

This will open a browser window for you to authenticate.

1.3 Update wrangler.toml

Edit wrangler.toml and replace the placeholder database_id with your actual D1 database ID.

Step 2: Set Up Slack App

Option A: Using manifest.yaml (Recommended)

  1. Go to https://api.slack.com/apps
  2. Click Create New App -> From an app manifest
  3. Select your workspace
  4. Copy the contents of manifest.yaml from this repo
  5. Paste it and click Create
  6. Update the Request URLs in the manifest with your actual worker URL:

Option B: Manual Configuration

  1. Go to https://api.slack.com/apps
  2. Click Create New App -> From scratch
  3. Name it "BLT-Lettuce" and select your workspace
  4. Configure OAuth and Permissions:
    • Add these Bot Token Scopes:
      • chat:write
      • im:write
      • im:read
      • im:history
      • channels:history
      • channels:read
      • users:read
      • team:read
  5. Enable Event Subscriptions:
  6. Enable Interactivity:

2.1 Get Your Credentials

  1. Go to OAuth and Permissions
  2. Install the app to your workspace
  3. Copy the Bot User OAuth Token (starts with xoxb-)
  4. Go to Basic Information
  5. Copy the Signing Secret

Step 3: Deploy to Cloudflare

3.1 Set Up Secrets

# Set your Slack Bot Token
wrangler secret put SLACK_TOKEN

# Set your Signing Secret
wrangler secret put SIGNING_SECRET

3.2 Optional Channel IDs

# Channel where join notifications are posted
wrangler secret put JOINS_CHANNEL_ID

# Channel ID for contribution guidelines
wrangler secret put CONTRIBUTE_ID

To find a channel ID in Slack:

  1. Right-click on the channel name
  2. Select Copy link
  3. The ID is the last part of the URL, for example: https://workspace.slack.com/archives/C06RMMRMGHE

3.3 Deploy the Worker

wrangler deploy

You will get a URL like: https://blt-lettuce-worker.your-subdomain.workers.dev

Step 4: Update Slack App URLs

If you created your app before deploying:

  1. Go back to your Slack app settings
  2. Update these URLs with your actual worker URL:
  3. Click Save Changes

Slack will verify the URL and you should see a green checkmark when configured correctly.

Step 5: Test Your Bot

  1. In your Slack workspace, invite a test user or create a new account
  2. The bot should automatically send them a welcome DM
  3. Try sending a message with the word "contribute" and the bot should respond
  4. Visit your worker URL in a browser to see the homepage

Deploying to Multiple Organizations

To allow other Slack workspaces to install your bot:

  1. In your Slack app settings, go to Manage Distribution
  2. Remove hard-coded information, if any
  3. Enable Org-Wide App Installation
  4. Share your app install link

Each organization will have its own isolated data in the Cloudflare D1 database.

Troubleshooting

URL Verification Failed

Bot Not Responding

  • Check that secrets are set: wrangler secret list
  • Verify the bot has the right permissions in Slack
  • Check logs: wrangler tail
  • Make sure the bot is installed in your workspace

Cannot Open DM with User

  • Make sure the bot has im:write permission
  • Verify the bot is installed in the workspace
  • Some users may have DMs disabled in their settings

Monitoring

View Logs

wrangler tail

View Homepage

Visit https://your-worker-url.workers.dev/ to see the full homepage with:

  • Live statistics
  • Bot features
  • GitHub project information

Updating the Worker

To update your worker after making changes:

wrangler deploy

The update is instant with no downtime required.

Cost

Cloudflare Workers free tier includes:

  • 100,000 requests per day
  • 10ms CPU time per request

For a typical Slack workspace, this is usually sufficient.

Support

If you encounter issues:

  1. Check the GitHub issues in this repository
  2. Review the Cloudflare Workers documentation
  3. Check the Slack API documentation

Security Notes

  • Never commit your secrets (SLACK_TOKEN, SIGNING_SECRET) to version control
  • The worker verifies all requests from Slack using HMAC signature validation
  • Replay attacks are prevented with timestamp validation
  • All secrets are stored securely in Cloudflare encrypted storage

πŸ“œ License

This project is licensed under the AGPL-3.0 License - see the LICENSE file for details.


πŸ™ Acknowledgments


Made with πŸ’š by the OWASP BLT Team

Join OWASP Slack Star this repo

About

An intelligent Slack bot for the OWASP community

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors