From 4626bbb00978d4f1ec48edcb406d0d56c0efcb66 Mon Sep 17 00:00:00 2001
From: VaiTon <eyadlorenzo@gmail.com>
Date: Mon, 7 Apr 2025 22:14:44 +0200
Subject: [PATCH 1/4] feat: allow specifying access_token as an env var

---
 ctfcli/__main__.py        |  6 +++++-
 ctfcli/core/api.py        |  7 ++++++-
 ctfcli/core/config.py     | 22 ++++++++++++++++++++++
 ctfcli/core/exceptions.py |  7 +++++++
 4 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/ctfcli/__main__.py b/ctfcli/__main__.py
index dcbf0a5..032618c 100644
--- a/ctfcli/__main__.py
+++ b/ctfcli/__main__.py
@@ -16,7 +16,7 @@
 from ctfcli.cli.pages import PagesCommand
 from ctfcli.cli.plugins import PluginsCommand
 from ctfcli.cli.templates import TemplatesCommand
-from ctfcli.core.exceptions import ProjectNotInitialized
+from ctfcli.core.exceptions import MissingAPIKey, ProjectNotInitialized
 from ctfcli.core.plugins import load_plugins
 from ctfcli.utils.git import check_if_dir_is_inside_git_repo
 
@@ -148,6 +148,10 @@ def main():
         if isinstance(ret, int):
             sys.exit(ret)
 
+    except MissingAPIKey as e:
+        click.secho(e, fg="red")
+        sys.exit(1)
+
     except ProjectNotInitialized:
         if click.confirm(
             "Outside of a ctfcli project, would you like to start a new project in this directory?",
diff --git a/ctfcli/core/api.py b/ctfcli/core/api.py
index 5487642..c9c62c5 100644
--- a/ctfcli/core/api.py
+++ b/ctfcli/core/api.py
@@ -3,6 +3,7 @@
 from requests import Session
 
 from ctfcli.core.config import Config
+from ctfcli.core.exceptions import MissingAPIKey
 
 
 class API(Session):
@@ -11,7 +12,11 @@ def __init__(self):
 
         # Load required configuration values
         self.url = config["config"]["url"]
-        self.access_token = config["config"]["access_token"]
+
+        try:
+            self.access_token = config["config"]["access_token"]
+        except KeyError:
+            raise MissingAPIKey()
 
         # Handle SSL verification disabling
         try:
diff --git a/ctfcli/core/config.py b/ctfcli/core/config.py
index 3e68d67..66103ff 100644
--- a/ctfcli/core/config.py
+++ b/ctfcli/core/config.py
@@ -10,6 +10,10 @@
 
 
 class Config:
+    _env_vars = {
+        "CTFD_TOKEN": "access_token",
+    }
+
     def __init__(self):
         self.base_path = self.get_base_path()
         self.project_path = self.get_project_path()
@@ -26,6 +30,24 @@ def __init__(self):
         self.config = parser
         self.challenges = dict(self.config["challenges"])
 
+        # Load environment variables
+        self._env_overrides()
+
+    def _env_overrides(self):
+        """
+        For each environment variable specified in _env_vars, check if it exists
+        and if so, add it to the config under the "config" section.
+        """
+        for env_var, config_key in self._env_vars.items():
+            env_value = os.getenv(env_var)
+            if not env_value:
+                continue
+
+            if not self.config.has_section("config"):
+                self.config.add_section("config")
+
+            self.config["config"][config_key] = env_value
+
     def __getitem__(self, key):
         return self.config[key]
 
diff --git a/ctfcli/core/exceptions.py b/ctfcli/core/exceptions.py
index bb5e4a4..fb6fa8c 100644
--- a/ctfcli/core/exceptions.py
+++ b/ctfcli/core/exceptions.py
@@ -3,6 +3,13 @@
 import click
 
 
+class MissingAPIKey(Exception):
+    def __str__(self):
+        return (
+            "Missing API key. Please set the API key in your configuration file or set CTFD_TOKEN environment variable."
+        )
+
+
 class ProjectNotInitialized(Exception):
     pass
 

From e251135b3c1ad7935f9e68be1efcf7052727de9d Mon Sep 17 00:00:00 2001
From: Kevin Chung <kchung@ctfd.io>
Date: Fri, 25 Apr 2025 02:13:23 -0400
Subject: [PATCH 2/4] Allow specifying URL via envvar update token envvar name

---
 ctfcli/core/config.py     | 3 ++-
 ctfcli/core/exceptions.py | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/ctfcli/core/config.py b/ctfcli/core/config.py
index 66103ff..028a7a0 100644
--- a/ctfcli/core/config.py
+++ b/ctfcli/core/config.py
@@ -11,7 +11,8 @@
 
 class Config:
     _env_vars = {
-        "CTFD_TOKEN": "access_token",
+        "CTFCLI_ACCESS_TOKEN": "access_token",
+        "CTFCLI_URL": "url",
     }
 
     def __init__(self):
diff --git a/ctfcli/core/exceptions.py b/ctfcli/core/exceptions.py
index fb6fa8c..ab69d7d 100644
--- a/ctfcli/core/exceptions.py
+++ b/ctfcli/core/exceptions.py
@@ -6,7 +6,7 @@
 class MissingAPIKey(Exception):
     def __str__(self):
         return (
-            "Missing API key. Please set the API key in your configuration file or set CTFD_TOKEN environment variable."
+            "Missing API key. Please set the API key in your configuration file or set CTFCLI_ACCESS_TOKEN environment variable."
         )
 
 

From 15a5009ed9c10a48a6a10f7ad331e6c4800b5982 Mon Sep 17 00:00:00 2001
From: Kevin Chung <kchung@ctfd.io>
Date: Fri, 25 Apr 2025 02:21:27 -0400
Subject: [PATCH 3/4] Show error if instance URL not configured

---
 ctfcli/__main__.py        | 10 +++++++++-
 ctfcli/core/api.py        |  7 +++++--
 ctfcli/core/exceptions.py | 11 ++++++++++-
 3 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/ctfcli/__main__.py b/ctfcli/__main__.py
index 032618c..d4467eb 100644
--- a/ctfcli/__main__.py
+++ b/ctfcli/__main__.py
@@ -16,7 +16,11 @@
 from ctfcli.cli.pages import PagesCommand
 from ctfcli.cli.plugins import PluginsCommand
 from ctfcli.cli.templates import TemplatesCommand
-from ctfcli.core.exceptions import MissingAPIKey, ProjectNotInitialized
+from ctfcli.core.exceptions import (
+    MissingAPIKey,
+    MissingInstanceURL,
+    ProjectNotInitialized,
+)
 from ctfcli.core.plugins import load_plugins
 from ctfcli.utils.git import check_if_dir_is_inside_git_repo
 
@@ -148,6 +152,10 @@ def main():
         if isinstance(ret, int):
             sys.exit(ret)
 
+    except MissingInstanceURL as e:
+        click.secho(e, fg="red")
+        sys.exit(1)
+
     except MissingAPIKey as e:
         click.secho(e, fg="red")
         sys.exit(1)
diff --git a/ctfcli/core/api.py b/ctfcli/core/api.py
index c9c62c5..f3b797a 100644
--- a/ctfcli/core/api.py
+++ b/ctfcli/core/api.py
@@ -3,7 +3,7 @@
 from requests import Session
 
 from ctfcli.core.config import Config
-from ctfcli.core.exceptions import MissingAPIKey
+from ctfcli.core.exceptions import MissingAPIKey, MissingInstanceURL
 
 
 class API(Session):
@@ -11,7 +11,10 @@ def __init__(self):
         config = Config()
 
         # Load required configuration values
-        self.url = config["config"]["url"]
+        try:
+            self.url = config["config"]["url"]
+        except KeyError:
+            raise MissingInstanceURL()
 
         try:
             self.access_token = config["config"]["access_token"]
diff --git a/ctfcli/core/exceptions.py b/ctfcli/core/exceptions.py
index ab69d7d..5c49d2d 100644
--- a/ctfcli/core/exceptions.py
+++ b/ctfcli/core/exceptions.py
@@ -6,7 +6,16 @@
 class MissingAPIKey(Exception):
     def __str__(self):
         return (
-            "Missing API key. Please set the API key in your configuration file or set CTFCLI_ACCESS_TOKEN environment variable."
+            "Missing API key. "
+            "Please set the API key in your configuration file or set the CTFCLI_ACCESS_TOKEN environment variable."
+        )
+
+
+class MissingInstanceURL(Exception):
+    def __str__(self):
+        return (
+            "Missing CTFd instance URL. "
+            "Please set the instance URL in your configuration file or set the CTFCLI_URL environment variable."
         )
 
 

From ea158a2084e82779d5b7a63327d7e497705f574d Mon Sep 17 00:00:00 2001
From: Kevin Chung <kchung@ctfd.io>
Date: Fri, 25 Apr 2025 02:32:27 -0400
Subject: [PATCH 4/4] update upload-artifact version

---
 .github/workflows/release.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bfe617c..cddca22 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,7 +33,7 @@ jobs:
       - name: Build package
         run: poetry build
 
-      - uses: actions/upload-artifact@v3
+      - uses: actions/upload-artifact@v4
         with:
           path: |
            ./dist/*.tar.gz