Overview
NestBot brings OWASP Nest capabilities directly into your Slack workspace. Query projects, chapters, events, and get AI-powered answers about OWASP without leaving Slack.
Available Commands
NestBot supports the following slash commands:
/owasp Gateway command to access all other commands
/ai Ask questions about OWASP (AI-powered)
/projects Search and browse OWASP projects
/chapters Find OWASP chapters worldwide
/events View upcoming OWASP events
/committees Explore OWASP committees
/community Access community resources
/leaders Find project and chapter leaders
/board View Board of Directors information
/staff Contact OWASP staff members
/sponsors View OWASP sponsors
/contribute Learn how to contribute to OWASP
/donate Support OWASP with donations
/jobs Browse security job opportunities
/gsoc Google Summer of Code information
/news Latest OWASP news and updates
/policies View OWASP policies and guidelines
/contact Get OWASP contact information
Command Implementation
All commands are implemented in:
backend/apps/slack/commands/
Command Structure
# backend/apps/slack/commands/__init__.py
from . import (
ai,
board,
chapters,
committees,
community,
contact,
contribute,
donate,
events,
gsoc,
jobs,
leaders,
news,
owasp,
policies,
projects,
sponsor,
sponsors,
staff,
users,
)
Core Commands
/owasp - Gateway Command
The /owasp command serves as a router to other commands:
/owasp projects security
# Equivalent to: /projects security
/owasp chapters london
# Equivalent to: /chapters london
/owasp help
# Shows available commands
Implementation:
# backend/apps/slack/commands/owasp.py:8
class Owasp ( CommandBase ):
"""Slack bot /owasp command."""
def find_command ( self , command_name : str ):
"""Find the command class by name."""
if not command_name:
return None
for cmd_class in ( cls for cls in CommandBase.get_commands() if cls is not Owasp):
if cmd_class. __name__ .lower() == command_name.lower():
return cmd_class()
return None
/projects - Project Search
Search OWASP projects from Slack:
/projects # List projects
/projects security # Search for "security" projects
/projects zap # Find ZAP project
Features:
Returns up to 10 matching projects
Shows project metadata (stars, level, etc.)
Includes 80-char name truncation
300-char summary truncation
Feedback and timestamp display
Direct links to project pages
Implementation:
# backend/apps/slack/commands/projects.py:8
class Projects ( CommandBase ):
def render_blocks ( self , command : dict ):
return get_blocks(
search_query = command[ "text" ].strip(),
limit = 10 ,
presentation = EntityPresentation(
include_feedback = True ,
include_metadata = True ,
include_pagination = False ,
include_timestamps = True ,
name_truncation = 80 ,
summary_truncation = 300 ,
),
)
/chapters - Chapter Discovery
Find OWASP chapters:
/chapters # List all chapters
/chapters london # Search for London chapter
/chapters start # Interactive start message
/chapters help # Show help
Features:
Same presentation format as projects
Location-based search
Leader information
Chapter activity metrics
Implementation:
# backend/apps/slack/commands/chapters.py:9
class Chapters ( CommandBase ):
def render_blocks ( self , command : dict ):
command_text = command[ "text" ].strip()
if command_text in COMMAND_HELP :
return super ().render_blocks(command)
return get_blocks(
search_query = "" if command_text in COMMAND_START else command_text,
limit = 10 ,
presentation = EntityPresentation(
include_feedback = True ,
include_metadata = True ,
include_pagination = False ,
include_timestamps = True ,
name_truncation = 80 ,
summary_truncation = 300 ,
),
)
/events - Event Listings
View upcoming OWASP events:
/events # Show upcoming events
Features:
Shows events sorted by start date
Displays event name, dates, location
Includes event descriptions
Links to registration pages
Direct link to full events page
Implementation:
# backend/apps/slack/commands/events.py:25
class Events ( CommandBase ):
def get_context ( self , command ):
return {
** super ().get_context(command),
"EVENTS" : get_events_data(),
"EVENTS_PAGE_NAME" : "OWASP Events" ,
"EVENTS_PAGE_URL" : f " { OWASP_URL } /events/" ,
}
/ai - AI-Powered Q&A
Ask questions about OWASP:
/ai What is OWASP ZAP?
/ai How do I contribute to OWASP projects?
/ai What are the OWASP Top 10?
Features:
Agentic RAG (Retrieval-Augmented Generation)
Question detection to filter non-OWASP queries
Iterative refinement for better answers
Context from projects, chapters, events, repositories
Markdown-formatted responses
Implementation:
# backend/apps/slack/commands/ai.py:6
class Ai ( CommandBase ):
def render_blocks ( self , command : dict ):
from apps.slack.common.handlers.ai import get_blocks
return get_blocks( query = command[ "text" ].strip())
AI Handler:
# backend/apps/slack/common/handlers/ai.py:14
def get_blocks ( query : str ) -> list[ dict ]:
ai_response = process_ai_query(query.strip())
if ai_response:
return [markdown(ai_response)]
return get_error_blocks()
def process_ai_query ( query : str ) -> str | None :
question_detector = QuestionDetector()
if not question_detector.is_owasp_question( text = query):
return get_default_response()
agent = AgenticRAGAgent()
result = agent.run( query = query)
return result[ "answer" ]
The AI command includes question detection to ensure queries are OWASP-related. Non-OWASP questions receive a polite redirect.
Explore OWASP committees:
/committees # List committees
/committees project # Search project committee
Implementation:
# backend/apps/slack/commands/committees.py:8
class Committees ( CommandBase ):
def render_blocks ( self , command : dict ):
return get_blocks(
search_query = command[ "text" ].strip(),
limit = 10 ,
presentation = EntityPresentation(
include_feedback = True ,
include_metadata = True ,
include_pagination = False ,
include_timestamps = True ,
name_truncation = 80 ,
summary_truncation = 300 ,
),
)
Access community information:
/community # View community page info
Implementation:
# backend/apps/slack/commands/community.py:7
class Community ( CommandBase ):
def get_context ( self , command : dict ):
return {
** super ().get_context(command),
"COMMUNITY_PAGE_NAME" : "OWASP community" ,
"COMMUNITY_PAGE_URL" : get_absolute_url( "/members" ),
}
Command Base Class
All commands inherit from CommandBase:
# backend/apps/slack/commands/command.py:20
class CommandBase :
"""Base class for Slack commands."""
@ property
def command_name ( self ) -> str :
"""Get the command name."""
return f "/ { self . __class__ . __name__ .lower() } "
def render_blocks ( self , command ):
"""Get the rendered blocks."""
blocks = []
for section in self .render_text( self .get_context(command)).split( SECTION_BREAK ):
if section.strip() == DIVIDER :
blocks.append({ "type" : "divider" })
elif section:
blocks.append(markdown(section))
return blocks
def handler ( self , ack , command , client ):
"""Handle the Slack command."""
ack()
if not settings. SLACK_COMMANDS_ENABLED :
return
try :
if blocks := self .render_blocks(command):
client.chat_postMessage(
blocks = blocks,
channel = client.conversations_open(
users = self .get_user_id(command),
)[ "channel" ][ "id" ],
text = get_text(blocks),
)
except Exception :
logger.exception( "Failed to handle command ' %s '" , self .command_name)
Entity Presentation
Commands use standardized presentation configuration:
EntityPresentation(
include_feedback = True , # Show feedback prompts
include_metadata = True , # Display entity metadata
include_pagination = False , # Disable pagination in Slack
include_timestamps = True , # Show creation/update times
name_truncation = 80 , # Max name length
summary_truncation = 300 , # Max summary length
)
Responses use Slack’s Block Kit:
# Markdown text block
markdown( "**Project Name** \n Project description here" )
# Divider
{ "type" : "divider" }
# Section break
SECTION_BREAK
Templates
Commands use Jinja2 templates:
backend/apps/slack/templates/commands/
Available Templates:
ai.jinja
board.jinja
chapters.jinja
community.jinja
contact.jinja
contribute.jinja
donate.jinja
events.jinja
gsoc.jinja
jobs.jinja
leaders.jinja
news.jinja
owasp.jinja
policies.jinja
sponsors.jinja
staff.jinja
Configuration
Commands are registered at startup:
# backend/apps/slack/commands/__init__.py:27
if SlackConfig.app:
CommandBase.configure_commands()
Environment Variables
SLACK_COMMANDS_ENABLED = true # Enable/disable commands
SLACK_BOT_TOKEN = xoxb-... # Bot user OAuth token
SLACK_SIGNING_SECRET = ... # Request verification
Error Handling
Commands include graceful error handling:
try :
if blocks := self .render_blocks(command):
client.chat_postMessage( ... )
except Exception :
logger.exception( "Failed to handle command ' %s '" , self .command_name)
blocks = [markdown( ":warning: An error occurred. Please try again later." )]
client.chat_postMessage( ... )
Usage Tips
Use /owasp help to see all available commands and their usage.
Commands are private - only you see the responses in your DM with NestBot.
The /ai command works best with specific, OWASP-related questions.
Code Reference
Key implementation files:
Commands: backend/apps/slack/commands/
Base Class: backend/apps/slack/commands/command.py:20
AI Handler: backend/apps/slack/common/handlers/ai.py:14
Templates: backend/apps/slack/templates/commands/
AI Insights - Learn about the AI system powering /ai
Projects - Project data returned by /projects
Chapters - Chapter data returned by /chapters
Events - Event data returned by /events