Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
bff36bd
rag tool for agent
Dishant1804 Jul 22, 2025
f254af8
code rabbit suggestions implemented
Dishant1804 Jul 22, 2025
a1bba29
Merge branch 'main' into RAG
Dishant1804 Jul 22, 2025
ad3f3b4
Merge branch 'main' into RAG
arkid15r Jul 22, 2025
c1334a6
Merge branch 'main' into RAG
Dishant1804 Jul 23, 2025
c9d4a27
Merge branch 'main' into RAG
Dishant1804 Jul 24, 2025
ff45de1
suggestions implemented
Dishant1804 Jul 25, 2025
4b38f5a
Merge remote-tracking branch 'upstream/main' into RAG
Dishant1804 Jul 25, 2025
b2c5b59
code rabbit suggestion
Dishant1804 Jul 25, 2025
9b94aed
Merge branch 'main' into RAG
Dishant1804 Jul 25, 2025
3038f32
Merge remote-tracking branch 'upstream/main' into RAG
Dishant1804 Jul 28, 2025
e120962
added context model
Dishant1804 Jul 28, 2025
f24453a
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Jul 29, 2025
e876a0c
retrieving data from context model
Dishant1804 Jul 29, 2025
981277a
removed try except
Dishant1804 Jul 29, 2025
8b46f08
Suggestions implemented
Dishant1804 Jul 30, 2025
16fabcf
code rabbit suggestion
Dishant1804 Jul 30, 2025
532be09
Merge branch 'main' into context-model
Dishant1804 Jul 30, 2025
77203b8
removed deafult
Dishant1804 Jul 30, 2025
9e03b53
updated tests
Dishant1804 Jul 30, 2025
ed44239
Merge branch 'main' into context-model
Dishant1804 Aug 4, 2025
41f8126
de coupled context and chunks
Dishant1804 Aug 5, 2025
c5aba9c
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Aug 5, 2025
697a406
update method for context
Dishant1804 Aug 7, 2025
46cd884
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Aug 7, 2025
a3255ff
major revamp and test cases
Dishant1804 Aug 9, 2025
64c079a
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Aug 9, 2025
7affa22
code rabbit suggestions
Dishant1804 Aug 10, 2025
55132d7
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Aug 10, 2025
3d7bd48
major revamp
Dishant1804 Aug 10, 2025
7d0731b
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Aug 10, 2025
ff3e61a
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Aug 12, 2025
c709b9e
suggestions implemented
Dishant1804 Aug 13, 2025
1c7fe1c
refactoring
Dishant1804 Aug 13, 2025
948c529
more tests
Dishant1804 Aug 13, 2025
1455083
Merge branch 'main' into context-model
Dishant1804 Aug 13, 2025
1e8d65e
more refactoring
Dishant1804 Aug 13, 2025
3f15d7a
Merge branch 'main' into context-model
Dishant1804 Aug 13, 2025
742a15e
Merge branch 'main' into context-model
Dishant1804 Aug 14, 2025
bd8f280
suggestions implemented
Dishant1804 Aug 14, 2025
8610dde
Merge branch 'main' into context-model
Dishant1804 Aug 14, 2025
a9da28b
chunk model update
Dishant1804 Aug 14, 2025
a0ed311
update logic and suggestions
Dishant1804 Aug 16, 2025
9646366
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Aug 16, 2025
2d86dcb
code rabbit suggestions
Dishant1804 Aug 16, 2025
011e843
before tests and question
Dishant1804 Aug 17, 2025
466bca3
sugesstions and decoupling with tests
Dishant1804 Aug 18, 2025
9c2556c
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Aug 18, 2025
c9f260d
Merge branch 'main' into context-model
Dishant1804 Aug 18, 2025
197c0ff
sugesstions implemented
Dishant1804 Aug 18, 2025
4dc3800
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Aug 18, 2025
346d324
Update code
arkid15r Aug 20, 2025
baae5eb
updated code
Dishant1804 Aug 21, 2025
f6bb1bd
spelling fixes
Dishant1804 Aug 21, 2025
6c353d1
Merge remote-tracking branch 'upstream/main' into context-model
Dishant1804 Aug 21, 2025
506ad46
test changes
Dishant1804 Aug 21, 2025
18937c3
slash command and auto reply
Dishant1804 Aug 21, 2025
7792ab5
nestbot mention auto reply
Dishant1804 Aug 21, 2025
6bc4443
tests
Dishant1804 Aug 21, 2025
f35bf4c
Merge remote-tracking branch 'upstream/main' into MVP
Dishant1804 Aug 21, 2025
2a8bbb6
poetry lock changes
Dishant1804 Aug 21, 2025
1cbcc28
Merge remote-tracking branch 'upstream/main' into MVP
Dishant1804 Aug 23, 2025
9c6fdd6
code rabbit suggestions and local yaml
Dishant1804 Aug 23, 2025
929989d
spelling fixes
Dishant1804 Aug 23, 2025
9580d97
Update code
arkid15r Sep 3, 2025
7370b16
git status
Dishant1804 Sep 6, 2025
337e9d4
poetry lock
Dishant1804 Sep 6, 2025
4509003
Merge branch 'feature/nestbot-assistant' into MVP
Dishant1804 Sep 6, 2025
647edba
poetry lock updated
Dishant1804 Sep 6, 2025
b7628a2
code rabbit suggesitons implemented
Dishant1804 Sep 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/apps/ai/common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
DEFAULT_SIMILARITY_THRESHOLD = 0.4
DELIMITER = "\n\n"
MIN_REQUEST_INTERVAL_SECONDS = 1.2
QUEUE_RESPONSE_TIME_MINUTES = 1
9 changes: 9 additions & 0 deletions backend/apps/slack/MANIFEST.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ features:
description: OWASP users list
usage_hint: <user>
should_escape: false
- command: /ai
url: https://nest.owasp.org/integrations/slack/commands/
description: AI-powered OWASP Nest assistant
usage_hint: <your question>
should_escape: false
oauth_config:
scopes:
user:
Expand All @@ -103,6 +108,7 @@ oauth_config:
- mpim:read
- users:read
bot:
- app_mentions:read
- channels:read
- chat:write
- commands
Expand All @@ -115,6 +121,7 @@ oauth_config:
- users:read
- groups:write
- channels:manage
- channels:history
settings:
event_subscriptions:
request_url: https://nest.owasp.org/integrations/slack/events/
Expand All @@ -123,7 +130,9 @@ settings:
- team_join
bot_events:
- app_home_opened
- app_mention
- member_joined_channel
- message.channels
- team_join
interactivity:
is_enabled: true
Expand Down
1 change: 1 addition & 0 deletions backend/apps/slack/admin/conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class ConversationAdmin(admin.ModelAdmin):
"is_private",
"is_archived",
"is_general",
"is_nest_bot_assistant_enabled",
)
},
),
Expand Down
7 changes: 7 additions & 0 deletions backend/apps/slack/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ class SlackConfig(AppConfig):
else None
)

def ready(self):
"""Configure Slack events when the app is ready."""
super().ready()
from apps.slack.events import configure_slack_events

configure_slack_events()


if SlackConfig.app:

Expand Down
1 change: 1 addition & 0 deletions backend/apps/slack/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from apps.slack.commands.command import CommandBase

from . import (
ai,
board,
chapters,
committees,
Expand Down
23 changes: 23 additions & 0 deletions backend/apps/slack/commands/ai.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Slack bot AI command."""

from apps.slack.commands.command import CommandBase


class Ai(CommandBase):
"""Slack bot /ai command."""

def render_blocks(self, command: dict):
"""Get the rendered blocks.

Args:
command (dict): The Slack command payload.

Returns:
list: A list of Slack blocks representing the AI response.

"""
from apps.slack.common.handlers.ai import get_blocks

return get_blocks(
query=command["text"].strip(),
)
61 changes: 61 additions & 0 deletions backend/apps/slack/common/handlers/ai.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Handler for AI-powered Slack functionality."""

from __future__ import annotations

import logging

from apps.ai.agent.tools.rag.rag_tool import RagTool
from apps.slack.blocks import markdown

logger = logging.getLogger(__name__)


def get_blocks(query: str) -> list[dict]:
"""Get AI response blocks.

Args:
query (str): The user's question.
presentation (EntityPresentation | None): Configuration for entity presentation.

Returns:
list: A list of Slack blocks representing the AI response.

"""
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:
"""Process the AI query using the RAG tool.

Args:
query (str): The user's question.

Returns:
str | None: The AI response or None if error occurred.

"""
rag_tool = RagTool(
chat_model="gpt-4o",
embedding_model="text-embedding-3-small",
)

return rag_tool.query(question=query)


def get_error_blocks() -> list[dict]:
"""Get error response blocks.

Returns:
list: A list of Slack blocks with error message.

"""
return [
markdown(
"⚠️ Unfortunately, I'm unable to answer your question at this time.\n"
"Please try again later or contact support if the issue persists."
)
]
57 changes: 57 additions & 0 deletions backend/apps/slack/common/question_detector.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both is_question and is_owasp_question logic design is questionable. We can't afford manually manage a list of terms indicating question intent and OWASP context. It's not scalable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you suggest alternative for it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some ideas, however it'd be great to see your suggestions first

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Question detection utilities for Slack OWASP bot."""

from __future__ import annotations

import logging
import re

from apps.slack.constants import OWASP_KEYWORDS

logger = logging.getLogger(__name__)


class QuestionDetector:
"""Utility class for detecting OWASP-related questions."""

def __init__(self):
"""Initialize the question detector."""
self.owasp_keywords = OWASP_KEYWORDS

self.question_patterns = [
r"\?",
r"^(what|how|why|when|where|which|who|can|could|would|should|is|are|does|do|did)",
r"(help|explain|tell me|show me|guide|tutorial|example)",
r"(recommend|suggest|advice|opinion)",
]

self.compiled_patterns = [
re.compile(pattern, re.IGNORECASE) for pattern in self.question_patterns
]

def is_owasp_question(self, text: str) -> bool:
"""Check if text contains an OWASP-related question."""
if not text or not text.strip():
return False

text_lower = text.lower().strip()

is_a_question = self.is_question(text_lower)
if not is_a_question:
return False

return self.contains_owasp_keywords(text_lower)

def is_question(self, text: str) -> bool:
"""Check if text appears to be a question."""
return any(pattern.search(text) for pattern in self.compiled_patterns)

def contains_owasp_keywords(self, text: str) -> bool:
"""Check if text contains OWASP-related keywords."""
words = re.findall(r"\b\w+\b", text)
text_words = set(words)

intersection = self.owasp_keywords.intersection(text_words)
if intersection:
return True

return any(" " in keyword and keyword in text for keyword in self.owasp_keywords)
57 changes: 57 additions & 0 deletions backend/apps/slack/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,63 @@
OWASP_SPONSORSHIP_CHANNEL_ID = "#C08EGFDD9L2"
OWASP_THREAT_MODELING_CHANNEL_ID = "#C1CS3C6AF"

OWASP_KEYWORDS = {
"owasp",
"security",
"vulnerability",
"vulnerabilities",
"zap",
"appsec",
"devsecops",
"nettacker",
"nest",
"threat modeling",
"top 10",
"top10",
"webgoat",
"dependency",
"secure",
"penetration",
"project",
"chapter",
"event",
"committee",
"defect dojo",
"juice shop",
"red team",
"injection",
"xss",
"csrf",
"authentication",
"authorization",
"encryption",
"cryptography",
"threat",
"risk",
"assessment",
"code review",
"static analysis",
"dynamic analysis",
"firewall",
"application security",
"web security",
"mobile security",
"api security",
"devops",
"secure coding",
"security testing",
"security tools",
"security framework",
"security guideline",
"security standard",
"security best practice",
"security policy",
"security bug",
"security patch",
"security update",
"security fix",
}

OWASP_WORKSPACE_ID = "T04T40NHX"

VIEW_PROJECTS_ACTION = "view_projects_action"
Expand Down
25 changes: 19 additions & 6 deletions backend/apps/slack/events/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
from apps.slack.apps import SlackConfig
from apps.slack.events import app_home_opened, team_join, url_verification
from apps.slack.events.event import EventBase
from apps.slack.events.member_joined_channel import catch_all, contribute, gsoc, project_nest
def configure_slack_events():
"""Configure Slack events after Django apps are ready."""
from apps.slack.apps import SlackConfig
from apps.slack.events import (
app_home_opened,
app_mention,
message_posted,
team_join,
url_verification,
)
from apps.slack.events.event import EventBase
from apps.slack.events.member_joined_channel import (
catch_all,
contribute,
gsoc,
project_nest,
)

if SlackConfig.app:
EventBase.configure_events()
if SlackConfig.app:
EventBase.configure_events()
43 changes: 43 additions & 0 deletions backend/apps/slack/events/app_mention.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Slack app mention event handler."""

import logging

from apps.slack.common.handlers.ai import get_blocks
from apps.slack.events.event import EventBase

logger = logging.getLogger(__name__)


class AppMention(EventBase):
"""Handles app mention events when the bot is mentioned in a channel."""

event_type = "app_mention"

def handle_event(self, event, client):
"""Handle an incoming app mention event."""
channel_id = event.get("channel")
text = event.get("text", "")

query = text
for mention in event.get("blocks", []):
if mention.get("type") == "rich_text":
for element in mention.get("elements", []):
if element.get("type") == "rich_text_section":
for text_element in element.get("elements", []):
if text_element.get("type") == "text":
query = text_element.get("text", "").strip()
break

if not query:
logger.warning("No query found in app mention")
return

logger.info("Handling app mention")

reply_blocks = get_blocks(query=query)
client.chat_postMessage(
channel=channel_id,
blocks=reply_blocks,
text=query,
thread_ts=event.get("thread_ts") or event.get("ts"),
)
Loading