Skip to content

Commit

Permalink
Merge pull request #127 from SaintsSec/edge
Browse files Browse the repository at this point in the history
v1.0.0
  • Loading branch information
AlexKollar authored Feb 19, 2025
2 parents cb9df1e + 518ddbf commit 738d2ae
Show file tree
Hide file tree
Showing 28 changed files with 1,290 additions and 119 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
__pycache__/
.vscode/settings.json
.DS_Store
.idea/
*.log
navienv/
verification_test
.navi_version
.navi_history
config
qodana.yaml
memories/
data/
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center">
<h1> Navi | CLI - An innovation in cybersec AI</h1>
<h1>v0.6.5 - "CSI - Con"</h1>
<h1>v0.6.6 - "Specter"</h1>
</div>

## 🤝 Sponsors / Endorsements: Thank you so much!
Expand All @@ -15,14 +15,11 @@

## **Key Features of Navi v0.6.5**

- **Upgraded Navi Shell** - The shell can now execute system commands, without breaking the flow of conversation. See more below!
- **Navi chips Upgrade** - The new alias variable within the custom scripts allow for Navi to execute scripts right from the chat. Once again not disrupting the flow.
- **Chip Creators Guide** - We are in the process of streamlining documentation on making custom chips.
- **Navi Nmap Chip** - We moved the Nmap Script over to being its own chip.
- **Navi3b** - The first iteration of our local AI system. Currently running Llama3.2-3b with ollama in the background.
- **Streamlined 3b install** - The install process is included the first time you launch navi (from edge currently) `navi --edge`
- **Navi chips Upgrade** - The chips system has been upgraded yet again with some qol, as well as a chip creator + template
- **Chip Creators Guide** - The chip documentation has been updated to reflect recent changes to the sytem.
- **Wiki Re-write** - With new power comes new documentation
- **Llama3.2 Integration** - We are running Meta's Llama AI on the backend.
- **[Navi Mind](https://github.com/SaintsSec/Navi-Mind)** - Planning phase of the first Navi AI Model we are calling *Navi Mind*.
- **[Navi Public Training](https://github.com/SaintsSec/Navi-Training)** - We want to be transparent about the data going into Navi. So we are building out a whole repo just for that.

### **Work In Progress**

Expand Down
Empty file added chips/Com/Community-Chips
Empty file.
Empty file added chips/Dev/Developer-Chips
Empty file.
Empty file added chips/SSG/SSG-Chips
Empty file.
50 changes: 50 additions & 0 deletions chips/SSG/chip-template.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import navi_internal

# Chip documentation: https://github.com/SaintsSec/Navi/wiki/4.-Developing-Chips-%E2%80%90-Indepth

command: str = "{{CHIP_NAME}}"
use: str = "What does this chip do?"
aliases: list = ['{{CHIP_NAME}}']
params: dict = {
'-help': 'Display help information',
'-h': 'Display help information',
}

help_params: tuple = ('-help', '-h')


def print_params() -> None:
# Print the header
print(f"{'Parameter':<10} | {'Description'}")
print("-" * 40)

# Print each dictionary item
for param, description in params.items():
print(f"{param:<10} | {description}")

# What Navi calls to run this Chip
def run(arguments=None) -> None:
# Get the instance of Navi. Required to access Navi-specific functions
navi_instance = navi_internal.navi_instance
navi_instance.print_message(f"How can I help you, {navi_instance.get_user()}?")

# To get a dictionary of current navi settings, and modify dictionary. Some modifications
# might require app restart. To prevent circular imports, keep this in a function or class.
#from navi_shell import get_navi_settings, modify_navi_settings

# Optional: Converts argument tokens into a list
arg_array = arguments.text.split()

# Remove the command itself
arg_array.pop(0)

# Optional: Check for parameters
if arg_array is not None:
for arg in arg_array:
match arg:
case x if x in help_params:
print_params()
return
case _:
navi_instance.print_message(f"Invalid parameter: {arg}")
return
178 changes: 178 additions & 0 deletions chips/SSG/navi_chip_creator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import os
import re
import subprocess # nosec
import sys
import webbrowser

from colorama import Fore

import navi_internal
from navi_shell import restart_navi

command: str = "chip-create"
use: str = "Creates a new Navi chip"
aliases: list = ['chip-create', 'cc']
params: dict[str, str] = {
'-help': 'Display help information',
'-h': 'Display help information',
}

help_params: tuple = ('-help', '-h')
template_file: str = "chips/SSG/chip-template.txt"
chip_install_path: str = "chips/Dev/"
chip_documentation_link: str = "https://github.com/SaintsSec/Navi/wiki/4.-Developing-Chips-%E2%80%90-Indepth"


def print_params() -> None:
"""Prints the available parameters and their descriptions."""
print(f"{'Parameter':<10} | {'Description'}")
print("-" * 40)
for param, description in params.items():
print(f"{param:<10} | {description}")


def get_user_input(prompt):
"""Gets input from the user."""
return input(prompt).strip()


def confirm_details(chip_name, chip_file_name, navi_instance):
"""Asks the user to confirm the chip details or make changes."""
while True:
navi_instance.print_message(f"Perfect! Here's a recap:"
f"\nChip Name: {chip_name}"
f"\nPython File Name: {chip_file_name}.py\n")
choice = get_user_input("Are you ready to proceed or do you want to make changes? "
f"({Fore.YELLOW}c{Fore.RESET})ontinue or ({Fore.YELLOW}m{Fore.RESET})ake changes: ").lower()
if choice == 'c':
return chip_name, chip_file_name
elif choice == 'm':
return make_changes(chip_name, chip_file_name, navi_instance)
else:
navi_instance.print_message("Invalid input. Please choose 'c' to continue or 'm' to make changes.")


def make_changes(chip_name, chip_file_name, navi_instance):
"""Allows the user to change the chip name or file name."""
while True:
change_choice = get_user_input(f"What do you want to change? ({Fore.YELLOW}chip{Fore.RESET}) name or "
f"({Fore.YELLOW}file{Fore.RESET}) name: ").lower()
if change_choice == 'chip':
navi_instance.print_message("Let's update the Chip Name.")
chip_name = get_user_input("New Chip Name: ")
return confirm_details(chip_name, chip_file_name, navi_instance)
elif change_choice == 'file':
navi_instance.print_message("Let's update the File Name.")
chip_file_name = get_user_input("New File Name: ")
return confirm_details(chip_name, chip_file_name, navi_instance)
else:
navi_instance.print_message("Invalid choice. Please enter 'chip' or 'file'.")


def create_chip_file(chip_name, chip_file_name):
"""Creates the chip file from a template."""
if not os.path.exists(template_file):
print(f"{Fore.RED}ERROR:{Fore.RESET} Template file '{template_file}' is missing. Aborting.")
return None

with open(template_file, "r") as template:
template_content = template.read()

chip_file_name_final = chip_file_name.replace(".py", "") + ".py"
template_content = template_content.replace("{{CHIP_NAME}}", chip_name)
chip_file_path = os.path.join(os.getcwd(), chip_install_path, chip_file_name_final)

with open(chip_file_path, "w") as chip_dev:
chip_dev.write(template_content)

print(f"{Fore.GREEN}Chip '{chip_name}' created successfully!{Fore.RESET}")
return chip_file_path


def post_creation_options(chip_file_path, navi_instance):
"""Provides options to the user after chip creation."""
navi_instance.print_message(f"Here are some options for you:\n"
f"{Fore.YELLOW}1{Fore.RESET}: Open directory of Chip location\n"
f"{Fore.YELLOW}2{Fore.RESET}: Open in your preferred code editor\n"
f"{Fore.YELLOW}3{Fore.RESET}: Reload Navi to load the new Chip\n"
f"{Fore.YELLOW}4{Fore.RESET}: Review the documentation online\n"
f"{Fore.YELLOW}5{Fore.RESET} or other value: I'm finished")
choice = get_user_input("Please enter 1, 2, 3, 4, or 5: ")
try:
if choice == '1':
print("Opening directory of Chip location")
directory = os.path.dirname(chip_file_path)
if sys.platform.startswith('win'):
subprocess.run(['explorer', directory], check=True) # nosec
elif sys.platform == 'darwin':
subprocess.run(['open', directory], check=True) # nosec
else:
subprocess.run(['xdg-open', directory], check=True) # nosec
elif choice == '2':
print("Opening in your preferred code editor")
if sys.platform.startswith('win'):
subprocess.run(['explorer', chip_file_path], check=True) # nosec
elif sys.platform == 'darwin':
subprocess.run(['open', chip_file_path], check=True) # nosec
else:
subprocess.run(['xdg-open', chip_file_path], check=True) # nosec
elif choice == '3':
restart_navi()
elif choice == '4':
webbrowser.open(chip_documentation_link)
else:
pass
except FileNotFoundError:
print("Couldn't find the file or directory.")
except subprocess.SubprocessError as e:
print(f"Failed to execute the command: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")


def sanitize_input(name):
"""
Sanitizes the user-provided chip name.
Ensures it is safe and valid for use in file paths.
"""
sanitized_name = name.strip()
if len(name) > 50:
print(f"{Fore.RED}Warning:{Fore.RESET} Input is too long (50 characters max). Trimming to 49 characters.")
sanitized_name = sanitized_name[:50]

# Replace invalid characters with underscores
old_name = sanitized_name
sanitized_name = re.sub(r'[^\w\-]', '_', name)
if old_name != sanitized_name:
print(f"{Fore.RED}Warning:{Fore.RESET} Invalid characters in '{old_name}'. Replacing with '{sanitized_name}'.")

return sanitized_name


def run(arguments=None):
navi_instance = navi_internal.navi_instance
arg_array = arguments.text.split()[1:] # Exclude the command itself

if arg_array:
for arg in arg_array:
if arg in help_params:
print_params()
return
else:
choice = get_user_input(f"Invalid parameter: {arg}\n"
f"Do you want to review the available parameters? (y/n): ").lower()
if choice == 'y':
print_params()
return

navi_instance.print_message(
f"Welcome to Navi Chip creator, {navi_instance.get_user()}. Please enter the name of your new Chip.")
chip_name = sanitize_input(get_user_input("Chip Name: "))
navi_instance.print_message(
f"Great name, {navi_instance.get_user()}! What do you want the python file to be called?")
chip_file_name = sanitize_input(get_user_input("Chip File Name: "))

chip_name, chip_file_name = confirm_details(chip_name, chip_file_name, navi_instance)
chip_file_path = create_chip_file(chip_name, chip_file_name)
if chip_file_path:
post_creation_options(chip_file_path, navi_instance)
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import os
import sys
import requests
import zipfile
import shutil
import subprocess # nosec
import sys
import uuid
import navi_internal
import zipfile

import requests
from colorama import Fore

import navi_internal
from navi import get_parameters
from navi_shell import restart_navi

Expand Down Expand Up @@ -83,7 +84,7 @@ def download_and_extract(download_url):
return None, None


def copy_files_to_install_path(extracted_dir, install_path="/commands"):
def copy_files_to_install_path(extracted_dir, install_path="/chips/Com"):
installed_files = []
try:
for item in os.listdir(extracted_dir):
Expand Down Expand Up @@ -114,7 +115,7 @@ def install_requirements(extracted_dir):
os.remove(requirements_path)


def update_script(download_url, install_path="commands"):
def update_script(download_url, install_path="chips/Com"):
print("Downloading chip...")
extracted_dir, download_guid = download_and_extract(download_url)
if extracted_dir:
Expand Down Expand Up @@ -236,21 +237,39 @@ def get_installed_chips() -> list[dict[str, str]] | None:
return modules


def get_dev_chips():
import chips
dev_chips = []
for command_name, module in chips.modules.items():
if module.__name__.startswith("chips.Dev"):
command_aliases = getattr(module, 'aliases', [])
command_use = getattr(module, 'use', "")
dev_chips.append((command_name, command_use, command_aliases))
return dev_chips


def list_installed_chips() -> None:
chips = get_installed_chips()
if chips:
print("Installed Chips:")
print("Installed Community Chips:")
for module in chips:
print(f"- {module['name']} (Owner: {module['owner']}, Version: {module['version']})")
else:
print("No chips are installed.")
print("No Community Chips are installed.")
dev_chips = get_dev_chips()
if not dev_chips:
print("No Developer Chips are installed.")
else:
print("\nDeveloper Chips:")
for chip in dev_chips:
print(f"- {chip[0]} (Use: {chip[1]}, Aliases: {chip[2]})")


def about_chip(name) -> dict[str, str] | None:
log_file_path = "installed_chips.txt"

if not os.path.exists(log_file_path):
print("No chips are installed.")
print("No Community Chips are installed.")
return None

with open(log_file_path, 'r') as log_file:
Expand Down Expand Up @@ -290,7 +309,7 @@ def about_chip(name) -> dict[str, str] | None:
"latest_version": latest_version
}

print(f"The chip '{name}' is not installed.")
print(f"The Community Chip '{name}' is not installed.")
return None


Expand All @@ -310,9 +329,9 @@ def update_chip(chip_name: str) -> None:

def help_text() -> None:
navi.print_message("Chip Manager\n"
"chips [install | uninstall | search | update] [app/query]\n\n"
"List currently installed chips\n"
"chips list")
"chips [install | uninstall | search | update] [app/query]\n\n"
"List currently installed chips\n"
"chips list")


def run(arguments=None) -> None:
Expand Down
1 change: 0 additions & 1 deletion commands/navi_clear.py → chips/SSG/navi_clear.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/bin/python3
import navi_internal

command = "clear"
Expand Down
1 change: 0 additions & 1 deletion commands/navi_exit.py → chips/SSG/navi_exit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/bin/python3
import navi_internal

command = "exit"
Expand Down
4 changes: 2 additions & 2 deletions commands/navi_help.py → chips/SSG/navi_help.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/python3
import commands
import chips

command = "navi_help"
use = "Displays the help screen"
Expand All @@ -12,7 +12,7 @@ def run(arguments=None):
max_alias_length = 0
command_data = []

for command_name, module in commands.modules.items():
for command_name, module in chips.modules.items():
command_aliases = getattr(module, 'aliases', [])
command_use = getattr(module, 'use', "")

Expand Down
Loading

0 comments on commit 738d2ae

Please sign in to comment.