From b4b7d676030b3b74efee5c5946248a596ccb2276 Mon Sep 17 00:00:00 2001 From: Namelessh8te <151823032+Namelessh8te@users.noreply.github.com> Date: Sun, 26 Jul 2026 15:56:07 -0700 Subject: [PATCH 1/4] Add root-isolated online notebook environment --- .devcontainer/devcontainer.json | 35 ++++++ .devcontainer/requirements.txt | 3 + Interactive-1.ipynb | 118 ++++++++++++++++++ ONLINE_NOTEBOOK.md | 47 ++++++++ contents for hackertools.ipynb | 206 ++++++++++++++++++++++++++++++++ 5 files changed, 409 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/requirements.txt create mode 100644 Interactive-1.ipynb create mode 100644 ONLINE_NOTEBOOK.md create mode 100644 contents for hackertools.ipynb diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..785cd960 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,35 @@ +{ + "name": "hackingtool notebook", + "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bookworm", + "remoteUser": "root", + "containerUser": "root", + "updateRemoteUserUID": false, + "postCreateCommand": "python -m pip install --disable-pip-version-check -r .devcontainer/requirements.txt", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-toolsai.jupyter" + ], + "settings": { + "python.defaultInterpreterPath": "/usr/local/bin/python", + "jupyter.notebookFileRoot": "${workspaceFolder}" + } + } + }, + "forwardPorts": [ + 8888 + ], + "portsAttributes": { + "8888": { + "label": "JupyterLab", + "onAutoForward": "silent", + "visibility": "private" + } + }, + "hostRequirements": { + "cpus": 2, + "memory": "4gb", + "storage": "16gb" + } +} diff --git a/.devcontainer/requirements.txt b/.devcontainer/requirements.txt new file mode 100644 index 00000000..d9d7cb1c --- /dev/null +++ b/.devcontainer/requirements.txt @@ -0,0 +1,3 @@ +-r ../requirements.txt +ipykernel>=6.29 +jupyterlab>=4.2 diff --git a/Interactive-1.ipynb b/Interactive-1.ipynb new file mode 100644 index 00000000..afdb2a05 --- /dev/null +++ b/Interactive-1.ipynb @@ -0,0 +1,118 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Interactive hackingtool workspace\n", + "\n", + "This notebook is ready for **Run All** in the repository's GitHub Codespace. Commands run as `root` inside the isolated container, not on the Codespaces host.\n", + "\n", + "The notebook prepares a non-blocking launcher and a safe argument-based command helper. Run security tools only against systems you own or are explicitly authorized to test." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import subprocess\n", + "import sys\n", + "from pathlib import Path\n", + "from typing import Sequence\n", + "\n", + "\n", + "def find_repo_root(start: Path | None = None) -> Path:\n", + " \"\"\"Find the repository without depending on the notebook launch directory.\"\"\"\n", + " current = (start or Path.cwd()).resolve()\n", + " for candidate in (current, *current.parents):\n", + " if (candidate / \"hackingtool.py\").is_file():\n", + " return candidate\n", + " raise FileNotFoundError(\"Could not find hackingtool.py from the current directory\")\n", + "\n", + "\n", + "REPO_ROOT = find_repo_root()\n", + "IS_ROOT = (os.geteuid() == 0) if hasattr(os, \"geteuid\") else False\n", + "IS_ISOLATED_CONTAINER = Path(\"/.dockerenv\").exists() or bool(\n", + " os.environ.get(\"CODESPACES\") or os.environ.get(\"REMOTE_CONTAINERS\")\n", + ")\n", + "\n", + "\n", + "def run_command(\n", + " arguments: Sequence[str | os.PathLike[str]],\n", + " *,\n", + " cwd: Path = REPO_ROOT,\n", + " check: bool = True,\n", + ") -> subprocess.CompletedProcess[str]:\n", + " \"\"\"Run an explicit argument list without shell interpolation.\"\"\"\n", + " if not arguments:\n", + " raise ValueError(\"At least one command argument is required\")\n", + " command = [os.fspath(argument) for argument in arguments]\n", + " print(\"Running:\", subprocess.list2cmdline(command))\n", + " return subprocess.run(\n", + " command,\n", + " cwd=cwd,\n", + " check=check,\n", + " text=True,\n", + " )\n", + "\n", + "\n", + "def launch_hackingtool() -> subprocess.CompletedProcess[str]:\n", + " \"\"\"Launch the interactive CLI on demand; this is not called by Run All.\"\"\"\n", + " return run_command([sys.executable, REPO_ROOT / \"hackingtool.py\"], check=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run-All readiness check.\n", + "required_files = [\n", + " REPO_ROOT / \"hackingtool.py\",\n", + " REPO_ROOT / \"constants.py\",\n", + " REPO_ROOT / \"requirements.txt\",\n", + "]\n", + "missing_files = [path.name for path in required_files if not path.is_file()]\n", + "if missing_files:\n", + " raise FileNotFoundError(f\"Missing required project files: {missing_files}\")\n", + "\n", + "write_probe = REPO_ROOT / \".interactive-notebook-write-check\"\n", + "try:\n", + " write_probe.write_text(\"ok\", encoding=\"utf-8\")\n", + "finally:\n", + " write_probe.unlink(missing_ok=True)\n", + "\n", + "print(f\"Repository: {REPO_ROOT}\")\n", + "print(f\"Python: {sys.version.split()[0]}\")\n", + "print(f\"Root in container: {IS_ROOT}\")\n", + "print(f\"Container detected: {IS_ISOLATED_CONTAINER}\")\n", + "print(\"\\nRun All is complete. Call launch_hackingtool() when you want the interactive CLI.\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ONLINE_NOTEBOOK.md b/ONLINE_NOTEBOOK.md new file mode 100644 index 00000000..0993c8f4 --- /dev/null +++ b/ONLINE_NOTEBOOK.md @@ -0,0 +1,47 @@ +# Run the notebook online + +The repository includes a GitHub Codespaces configuration for both +`contents for hackertools.ipynb` and `Interactive-1.ipynb`. The Codespace runs +as `root` inside its isolated container. Root access applies only to the +container; it does not grant access to the Codespaces host. + +## Start in GitHub Codespaces + +1. Push this branch to a GitHub repository you control. +2. Open the repository on GitHub and select **Code → Codespaces → Create + codespace on this branch**. +3. Wait for the container setup to finish. +4. Open either notebook: + - `contents for hackertools.ipynb` for environment detection and explicit + system-package installation. + - `Interactive-1.ipynb` for the project-aware command helper and interactive + CLI launcher. +5. Select the **Python 3** kernel and choose **Run All**. + +The environment check should print: + +```text +system linux +is_root True +... +Ready: root permissions are available inside the isolated container. +``` + +## Optional JupyterLab server + +The browser-based Codespaces editor can run the notebook directly. If a +standalone JupyterLab UI is preferred, run this from the Codespaces terminal: + +```bash +jupyter lab --ip=0.0.0.0 --port=8888 --no-browser --allow-root +``` + +Codespaces forwards port 8888 privately by default. Keep it private and retain +Jupyter's generated access token. + +## Permission boundary + +The container intentionally does not use Docker `--privileged`, host filesystem +mounts, or a Docker socket mount. Those are not required for this notebook and +would weaken isolation. Use security tooling only on systems you own or have +explicit authorization to test. diff --git a/contents for hackertools.ipynb b/contents for hackertools.ipynb new file mode 100644 index 00000000..092f9b1e --- /dev/null +++ b/contents for hackertools.ipynb @@ -0,0 +1,206 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Hackingtool environment helper\n", + "\n", + "This notebook is ready for **Run All** in the repository's GitHub Codespace. The Codespace runs as `root` inside an isolated Linux container, so package installation is allowed without giving the notebook access to the host machine.\n", + "\n", + "`Run All` performs environment detection and a permissions check. System package installation remains explicit: add package names to `packages_to_install` in the last cell when needed. Use security tools only on systems you own or are authorized to test." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import platform\n", + "import re\n", + "import shutil\n", + "import subprocess\n", + "from dataclasses import asdict, dataclass, field\n", + "from pathlib import Path\n", + "\n", + "\n", + "@dataclass\n", + "class OSInfo:\n", + " system: str\n", + " distro_id: str = \"\"\n", + " distro_like: str = \"\"\n", + " distro_version: str = \"\"\n", + " pkg_manager: str = \"\"\n", + " is_root: bool = False\n", + " home_dir: Path = field(default_factory=Path.home)\n", + " is_wsl: bool = False\n", + " arch: str = \"\"\n", + "\n", + "\n", + "def detect() -> OSInfo:\n", + " \"\"\"Detect the operating system, Linux distribution, and package manager.\"\"\"\n", + " system = platform.system().lower()\n", + " if system == \"darwin\":\n", + " system = \"macos\"\n", + "\n", + " info = OSInfo(\n", + " system=system,\n", + " is_root=(os.geteuid() == 0) if hasattr(os, \"geteuid\") else False,\n", + " home_dir=Path.home(),\n", + " arch=platform.machine(),\n", + " )\n", + "\n", + " if system == \"linux\":\n", + " try:\n", + " info.is_wsl = \"microsoft\" in Path(\"/proc/version\").read_text(\n", + " encoding=\"utf-8\"\n", + " ).lower()\n", + " except (FileNotFoundError, PermissionError):\n", + " pass\n", + "\n", + " os_release: dict[str, str] = {}\n", + " for release_path in (Path(\"/etc/os-release\"), Path(\"/usr/lib/os-release\")):\n", + " try:\n", + " for line in release_path.read_text(encoding=\"utf-8\").splitlines():\n", + " key, separator, value = line.partition(\"=\")\n", + " if separator:\n", + " os_release[key.strip()] = value.strip().strip('\"')\n", + " break\n", + " except (FileNotFoundError, PermissionError):\n", + " continue\n", + "\n", + " info.distro_id = os_release.get(\"ID\", \"\").lower()\n", + " info.distro_like = os_release.get(\"ID_LIKE\", \"\").lower()\n", + " info.distro_version = os_release.get(\"VERSION_ID\", \"\")\n", + "\n", + " for manager in (\"apt-get\", \"pacman\", \"dnf\", \"zypper\", \"apk\", \"brew\", \"pkg\"):\n", + " if shutil.which(manager):\n", + " info.pkg_manager = manager\n", + " break\n", + "\n", + " return info\n", + "\n", + "\n", + "CURRENT_OS = detect()\n", + "\n", + "PACKAGE_INSTALL_ARGS: dict[str, list[str]] = {\n", + " \"apt-get\": [\"apt-get\", \"install\", \"-y\"],\n", + " \"pacman\": [\"pacman\", \"-S\", \"--noconfirm\"],\n", + " \"dnf\": [\"dnf\", \"install\", \"-y\"],\n", + " \"zypper\": [\"zypper\", \"install\", \"-y\"],\n", + " \"apk\": [\"apk\", \"add\"],\n", + " \"brew\": [\"brew\", \"install\"],\n", + " \"pkg\": [\"pkg\", \"install\", \"-y\"],\n", + "}\n", + "\n", + "REQUIRED_PACKAGES: dict[str, list[str]] = {\n", + " \"apt-get\": [\n", + " \"git\", \"python3-pip\", \"python3-venv\", \"curl\", \"wget\",\n", + " \"ruby\", \"ruby-dev\", \"golang-go\", \"php\", \"default-jre-headless\",\n", + " ],\n", + " \"pacman\": [\n", + " \"git\", \"python-pip\", \"curl\", \"wget\", \"ruby\", \"go\", \"php\",\n", + " \"jre-openjdk-headless\",\n", + " ],\n", + " \"dnf\": [\n", + " \"git\", \"python3-pip\", \"curl\", \"wget\", \"ruby\", \"golang\", \"php\",\n", + " \"java-17-openjdk-headless\",\n", + " ],\n", + " \"zypper\": [\"git\", \"python3-pip\", \"curl\", \"wget\", \"ruby\", \"go\", \"php\"],\n", + " \"brew\": [\"git\", \"python3\", \"curl\", \"wget\", \"ruby\", \"go\", \"php\"],\n", + " \"pkg\": [\"git\", \"python3\", \"py39-pip\", \"curl\", \"wget\", \"ruby\", \"go\", \"php83\"],\n", + "}\n", + "\n", + "_PACKAGE_NAME = re.compile(r\"^[A-Za-z0-9][A-Za-z0-9+_.:@/-]*$\")\n", + "\n", + "\n", + "def install_packages(packages: list[str], os_info: OSInfo | None = None) -> bool:\n", + " \"\"\"Install explicitly requested system packages without invoking a shell.\"\"\"\n", + " info = os_info or CURRENT_OS\n", + " if not packages:\n", + " return True\n", + " if info.pkg_manager not in PACKAGE_INSTALL_ARGS:\n", + " raise RuntimeError(f\"Unsupported package manager: {info.pkg_manager or 'none'}\")\n", + "\n", + " invalid = [package for package in packages if not _PACKAGE_NAME.fullmatch(package)]\n", + " if invalid:\n", + " raise ValueError(f\"Invalid package name(s): {invalid}\")\n", + "\n", + " command = [*PACKAGE_INSTALL_ARGS[info.pkg_manager], *packages]\n", + " if info.system == \"linux\" and not info.is_root:\n", + " privilege_command = shutil.which(\"doas\") or shutil.which(\"sudo\")\n", + " if not privilege_command:\n", + " raise PermissionError(\"Root, doas, or sudo is required for package installation\")\n", + " command.insert(0, privilege_command)\n", + "\n", + " completed = subprocess.run(command, check=False)\n", + " return completed.returncode == 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Verify the runtime used by Run All.\n", + "runtime = asdict(CURRENT_OS)\n", + "runtime[\"home_dir\"] = str(runtime[\"home_dir\"])\n", + "for key, value in runtime.items():\n", + " print(f\"{key:16} {value}\")\n", + "\n", + "write_probe = Path.cwd() / \".hackingtool-notebook-write-check\"\n", + "try:\n", + " write_probe.write_text(\"ok\", encoding=\"utf-8\")\n", + "finally:\n", + " write_probe.unlink(missing_ok=True)\n", + "\n", + "if CURRENT_OS.system == \"linux\" and CURRENT_OS.is_root:\n", + " print(\"\\nReady: root permissions are available inside the isolated container.\")\n", + "else:\n", + " print(\"\\nReady: notebook execution works; system installs may request elevation.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Optional: add only the system packages you intend to install, then rerun this cell.\n", + "# Example: packages_to_install = [\"git\", \"curl\"]\n", + "packages_to_install: list[str] = []\n", + "\n", + "if packages_to_install:\n", + " if not install_packages(packages_to_install):\n", + " raise RuntimeError(\"One or more packages failed to install\")\n", + " print(\"Installed:\", \", \".join(packages_to_install))\n", + "else:\n", + " print(\"No system packages requested; Run All is complete.\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From b74e26b0018b3e505433c04b6b0c38b69ccf4bd2 Mon Sep 17 00:00:00 2001 From: Namelessh8te <151823032+Namelessh8te@users.noreply.github.com> Date: Fri, 14 Aug 2026 21:23:59 -0700 Subject: [PATCH 2/4] Fix Codespace package installation --- .devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 785cd960..3361098a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,7 +4,7 @@ "remoteUser": "root", "containerUser": "root", "updateRemoteUserUID": false, - "postCreateCommand": "python -m pip install --disable-pip-version-check -r .devcontainer/requirements.txt", + "postCreateCommand": "python -m pip install --disable-pip-version-check -e . -r .devcontainer/requirements.txt", "customizations": { "vscode": { "extensions": [ From 5d6a7d14e15c11af13fff42a14ae4b2b35451ee8 Mon Sep 17 00:00:00 2001 From: Namelessh8te <151823032+Namelessh8te@users.noreply.github.com> Date: Fri, 14 Aug 2026 21:24:02 -0700 Subject: [PATCH 3/4] Remove obsolete root requirements include --- .devcontainer/requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/.devcontainer/requirements.txt b/.devcontainer/requirements.txt index d9d7cb1c..0bb2d8e0 100644 --- a/.devcontainer/requirements.txt +++ b/.devcontainer/requirements.txt @@ -1,3 +1,2 @@ --r ../requirements.txt ipykernel>=6.29 jupyterlab>=4.2 From 5c9aabafe0975629ab61897d1bac457275a40298 Mon Sep 17 00:00:00 2001 From: Namelessh8te <151823032+Namelessh8te@users.noreply.github.com> Date: Fri, 14 Aug 2026 21:24:16 -0700 Subject: [PATCH 4/4] Fix notebook for src-layout package --- Interactive-1.ipynb | 48 +++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/Interactive-1.ipynb b/Interactive-1.ipynb index afdb2a05..9e4f7246 100644 --- a/Interactive-1.ipynb +++ b/Interactive-1.ipynb @@ -17,20 +17,21 @@ "metadata": {}, "outputs": [], "source": [ + "import importlib.util\n", "import os\n", + "import shutil\n", "import subprocess\n", - "import sys\n", "from pathlib import Path\n", "from typing import Sequence\n", "\n", "\n", "def find_repo_root(start: Path | None = None) -> Path:\n", - " \"\"\"Find the repository without depending on the notebook launch directory.\"\"\"\n", + " \"\"\"Find the src-layout repository without depending on notebook launch directory.\"\"\"\n", " current = (start or Path.cwd()).resolve()\n", " for candidate in (current, *current.parents):\n", - " if (candidate / \"hackingtool.py\").is_file():\n", + " if (candidate / \"pyproject.toml\").is_file() and (candidate / \"src\" / \"hackingtool\").is_dir():\n", " return candidate\n", - " raise FileNotFoundError(\"Could not find hackingtool.py from the current directory\")\n", + " raise FileNotFoundError(\"Could not find pyproject.toml and src/hackingtool from the current directory\")\n", "\n", "\n", "REPO_ROOT = find_repo_root()\n", @@ -51,17 +52,15 @@ " raise ValueError(\"At least one command argument is required\")\n", " command = [os.fspath(argument) for argument in arguments]\n", " print(\"Running:\", subprocess.list2cmdline(command))\n", - " return subprocess.run(\n", - " command,\n", - " cwd=cwd,\n", - " check=check,\n", - " text=True,\n", - " )\n", + " return subprocess.run(command, cwd=cwd, check=check, text=True)\n", "\n", "\n", "def launch_hackingtool() -> subprocess.CompletedProcess[str]:\n", - " \"\"\"Launch the interactive CLI on demand; this is not called by Run All.\"\"\"\n", - " return run_command([sys.executable, REPO_ROOT / \"hackingtool.py\"], check=False)" + " \"\"\"Launch the installed interactive CLI on demand; this is not called by Run All.\"\"\"\n", + " executable = shutil.which(\"hackingtool\")\n", + " if executable is None:\n", + " raise RuntimeError(\"hackingtool CLI is not installed; rebuild the Codespace or run: python -m pip install -e .\")\n", + " return run_command([executable], check=False)\n" ] }, { @@ -71,14 +70,20 @@ "outputs": [], "source": [ "# Run-All readiness check.\n", - "required_files = [\n", - " REPO_ROOT / \"hackingtool.py\",\n", - " REPO_ROOT / \"constants.py\",\n", - " REPO_ROOT / \"requirements.txt\",\n", + "required_paths = [\n", + " REPO_ROOT / \"pyproject.toml\",\n", + " REPO_ROOT / \"src\" / \"hackingtool\" / \"cli.py\",\n", + " REPO_ROOT / \"src\" / \"hackingtool\" / \"constants.py\",\n", "]\n", - "missing_files = [path.name for path in required_files if not path.is_file()]\n", - "if missing_files:\n", - " raise FileNotFoundError(f\"Missing required project files: {missing_files}\")\n", + "missing_paths = [str(path.relative_to(REPO_ROOT)) for path in required_paths if not path.exists()]\n", + "if missing_paths:\n", + " raise FileNotFoundError(f\"Missing required project paths: {missing_paths}\")\n", + "\n", + "if importlib.util.find_spec(\"hackingtool\") is None:\n", + " raise RuntimeError(\"hackingtool package is not installed in this kernel; rebuild the Codespace or run: python -m pip install -e .\")\n", + "\n", + "if shutil.which(\"hackingtool\") is None:\n", + " raise RuntimeError(\"hackingtool console script is not on PATH\")\n", "\n", "write_probe = REPO_ROOT / \".interactive-notebook-write-check\"\n", "try:\n", @@ -87,10 +92,11 @@ " write_probe.unlink(missing_ok=True)\n", "\n", "print(f\"Repository: {REPO_ROOT}\")\n", - "print(f\"Python: {sys.version.split()[0]}\")\n", + "print(f\"Python package: {importlib.util.find_spec('hackingtool').origin}\")\n", + "print(f\"CLI: {shutil.which('hackingtool')}\")\n", "print(f\"Root in container: {IS_ROOT}\")\n", "print(f\"Container detected: {IS_ISOLATED_CONTAINER}\")\n", - "print(\"\\nRun All is complete. Call launch_hackingtool() when you want the interactive CLI.\")" + "print(\"\\nRun All is complete. Call launch_hackingtool() when you want the interactive CLI.\")\n" ] } ],