Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moved XDG config loading to entrypoint #242

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
The Changelog starts with v0.4.1, because we did not keep one before that,
and simply didn't have the time to go back and retroactively create one.

## [Unreleased]

### Changed
- Removed default configuration loading from `Manager` class.
- Added XDG-compliant configuration and module loading to the main entrypoint.

## [0.5.4] - 2022-01-27
Bug fix for the `load` command.

Expand Down
38 changes: 38 additions & 0 deletions pwncat/__main__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#!/usr/bin/env python3
import os
import sys
import logging
import argparse
import importlib.metadata
from pathlib import Path

from rich import box
from rich.table import Table
Expand Down Expand Up @@ -107,6 +109,42 @@ def main():
# Create the session manager
with pwncat.manager.Manager(args.config) as manager:

# Grab all data directories. The order of XDG_DATA_DIRS is reversed
# to ensure the importance is increasing.
data_dirs = [
Path(p)
for p in os.environ.get(
"XDG_DATA_DIRS", "/usr/local/share:/usr/share"
).split(":")
][::-1] + [Path(os.environ.get("XDG_DATA_HOME", "~/.local/share")).expanduser()]

# Grab all configuration directories. The order of XDG_CONFIG_DIRS is
# reverse to ensure importance is increasing. We also add `/etc/` because
# /etc/xdg is the default, but most people expect /etc.
config_dirs = (
[Path("/etc")]
+ [
Path(p)
for p in os.environ.get("XDG_CONFIG_DIRS", "/etc/xdg").split(":")
][::-1]
+ [Path(os.environ.get("XDG_CONFIG_HOME", "~/.config")).expanduser()]
)

# Attempt to load modules from all data directories
for data_dir in data_dirs:
manager.load_modules(str(data_dir / "pwncat" / "modules"))

# Attempt to load all configuration files
for config_dir in config_dirs:
config_path = config_dir / "pwncat" / "pwncatrc"
try:
with config_path.open() as filp:
manager.parser.eval(filp.read(), str(config_path))
except FileNotFoundError:
pass
except PermissionError as exc:
manager.log(f"ignoring config: {config_path}: {exc}")

if args.verbose:
# set the config variable `verbose` to `True` (globally)
manager.config.set("verbose", True, True)
Expand Down
38 changes: 0 additions & 38 deletions pwncat/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -821,51 +821,13 @@ def __init__(self, config: str = None):
# Load standard modules
self.load_modules(*pwncat.modules.__path__)

# Get our data directory
data_home = os.environ.get("XDG_DATA_HOME", "~/.local/share")
if not data_home:
data_home = "~/.local/share"

# Expand the user path
data_home = os.path.expanduser(os.path.join(data_home, "pwncat"))

# Find modules directory
modules_dir = os.path.join(data_home, "modules")

# Load local modules if they exist
if os.path.isdir(modules_dir):
self.load_modules(modules_dir)

# Load global configuration script, if available
try:
with open("/etc/pwncat/pwncatrc") as filp:
self.parser.eval(filp.read(), "/etc/pwncat/pwncatrc")
except (FileNotFoundError, PermissionError):
pass

# Load user configuration script
user_rc = os.path.join(data_home, "pwncatrc")
try:
with open(user_rc) as filp:
self.parser.eval(filp.read(), user_rc)
except (FileNotFoundError, PermissionError):
pass

# Load local configuration script
if isinstance(config, str):
with open(config) as filp:
self.parser.eval(filp.read(), config)
elif config is not None:
self.parser.eval(config.read(), getattr(config, "name", "fileobj"))
config.close()
else:
try:
# If no config is specified, attempt to load `./pwncatrc`
# but don't fail if it doesn't exist.
with open("./pwncatrc") as filp:
self.parser.eval(filp.read(), "./pwncatrc")
except (FileNotFoundError, PermissionError):
pass

if self.db is None:
self.open_database()
Expand Down
36 changes: 8 additions & 28 deletions tests/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,11 @@ def test_config_fileobj():

def test_user_config(tmp_path):

import os

# Ensure we don't muck up the environment for this process
old_home = os.environ.get("XDG_DATA_HOME", None)

try:
# Set the data home to our temp path
os.environ["XDG_DATA_HOME"] = str(tmp_path)

# Create the pwncat directory
(tmp_path / "pwncat").mkdir(exist_ok=True, parents=True)

# Create our user configuration
with (tmp_path / "pwncat" / "pwncatrc").open("w") as filp:
filp.writelines(["""set -g backdoor_user "config_test"\n"""])

os.chdir(tmp_path)

# Create a manager object with default config to load our
# user configuration.
with pwncat.manager.Manager(config=None) as manager:
assert manager.config["backdoor_user"] == "config_test"
finally:
# Restore the environment
if old_home is not None:
os.environ["XDG_DATA_HOME"] = old_home
else:
del os.environ["XDG_DATA_HOME"]
# Create our user configuration
with (tmp_path / "pwncatrc").open("w") as filp:
filp.writelines(["""set -g backdoor_user "config_test"\n"""])

# Create a manager object with default config to load our
# user configuration.
with pwncat.manager.Manager(config=str(tmp_path / "pwncatrc")) as manager:
assert manager.config["backdoor_user"] == "config_test"