Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

security: add whitelist validation for SQLite PRAGMA settings#1529

Open
mitre88 wants to merge 3 commits intoOWASP:masterOWASP/Nettacker:masterfrom
mitre88:fix/sql-pragma-whitelistmitre88/Nettacker:fix/sql-pragma-whitelistCopy head branch name to clipboard
Open

security: add whitelist validation for SQLite PRAGMA settings#1529
mitre88 wants to merge 3 commits intoOWASP:masterOWASP/Nettacker:masterfrom
mitre88:fix/sql-pragma-whitelistmitre88/Nettacker:fix/sql-pragma-whitelistCopy head branch name to clipboard

Conversation

@mitre88
Copy link
Copy Markdown

@mitre88 mitre88 commented Apr 24, 2026

Summary

Add whitelist validation for SQLite PRAGMA settings to prevent potential SQL injection.

Changes

  • nettacker/database/db.py: Add allowed_journal_modes and allowed_sync_modes whitelists
  • Validate Config.db.journal_mode and Config.db.synchronous_mode against whitelists
  • Raise ValueError if invalid values are provided

Why

Using f-strings in SQL PRAGMA statements is a dangerous pattern that could lead to SQL injection if these values ever come from user input. By using whitelists, we ensure only valid, expected values are accepted.

Open Source Contributor added 3 commits April 23, 2026 11:07
…ation

- Replace yaml.load() with yaml.safe_load() to prevent arbitrary code execution
- Add validation for database names to prevent SQL injection in CREATE DATABASE statements
- Use parameterized identifiers with backticks (MySQL) and quotes (PostgreSQL)
- Use context managers for all file open() calls to prevent resource leaks
- Fix load_yaml() to properly close file handles
- Fix file reading in targets_list, user_agents, usernames_list, passwords_list, and read_from_file
- Add allowed_journal_modes and allowed_sync_modes whitelists
- Validate Config.db.journal_mode and Config.db.synchronous_mode against whitelists
- Raise ValueError if invalid values are provided

This prevents potential SQL injection in PRAGMA statements.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened database creation and execution to validate and safely format database names across SQLite, MySQL, and PostgreSQL.
    • Enforced allowed SQLite configuration values for journal and synchronous settings.
  • Improvements

    • Safer file handling for input lists to ensure files are properly closed.
    • More reliable YAML configuration parsing.

Walkthrough

Argument-file reading now uses context managers; YAML loading uses yaml.safe_load directly from file. SQLite PRAGMA settings are validated against allowed values before applying. MySQL and PostgreSQL database creation validate and quote database names, raising ValueError for invalid names.

Changes

Cohort / File(s) Summary
File resource & YAML loading
nettacker/core/arg_parser.py, nettacker/core/messages.py
Argument and list files are opened with context managers to ensure closure. YAML parsing switched to yaml.safe_load on the opened file handle (removed StringIO + yaml.FullLoader).
SQLite PRAGMA validation
nettacker/database/db.py
Config.db.journal_mode and Config.db.synchronous_mode are checked against allowed value sets; invalid values raise ValueError before executing PRAGMA statements.
Database name validation & quoting
nettacker/database/mysql.py, nettacker/database/postgresql.py
Config.db.name is validated (alphanumeric, starts with a letter). Valid names are quoted in CREATE DATABASE (\`` for MySQL, "for PostgreSQL); invalid names causeValueError`.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title focuses specifically on SQLite PRAGMA whitelist validation, but the PR also includes file handle leak fixes, YAML security improvements, and database name validation for MySQL/PostgreSQL—changes that represent the bulk of the modifications and are not reflected in the title. Broaden the title to capture the primary security improvements across multiple files, such as: 'security: add input validation and resource management improvements' or list the main changes more comprehensively.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description accurately addresses the SQLite PRAGMA validation aspect mentioned in the title, explaining the rationale and changes related to that feature.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f7b4bcc79a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if Config.db.name not in existing_databases:
conn.execute(text("CREATE DATABASE {0} ".format(Config.db.name)))
db_name = Config.db.name
if db_name.isalnum() and db_name[0].isalpha():
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Permit underscores in MySQL database-name validation

The new check db_name.isalnum() and db_name[0].isalpha() rejects names like test_db, even though underscore-containing names are valid and already expected in this codebase (for example tests/database/test_mysql.py uses Config.db.name = "test_db"). With that input, mysql_create_database() now raises ValueError, so database initialization can fail for existing user configs that previously worked. The validation should still prevent injection but accept valid identifier characters such as _.

Useful? React with 👍 / 👎.

conn = conn.execution_options(isolation_level="AUTOCOMMIT")
conn.execute(text(f"CREATE DATABASE {Config.db.name}"))
db_name = Config.db.name
if db_name.isalnum() and db_name[0].isalpha():
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Allow valid PostgreSQL names with underscores

The same validation pattern in PostgreSQL setup now rejects common valid names like nettacker_db (which this repo itself uses in tests/database/test_postgresql.py). In the fallback path that creates a missing database, this causes a ValueError instead of creating the DB, so first-run setup breaks for underscore-based names that were previously accepted. Keep injection hardening, but broaden validation to include valid PostgreSQL identifier characters.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
nettacker/core/messages.py (1)

22-24: Consider specifying encoding="utf-8" when loading language YAML files.

load_yaml is used primarily to load translation files under Config.path.locale_dir, which contain non-ASCII characters (e.g., CJK, Arabic, Persian, Cyrillic). Relying on the platform default encoding means this call can fail or decode incorrectly on systems where the default is not UTF-8 (notably Windows with cp1252/cp932/etc.). Pinning the encoding makes behavior deterministic across platforms.

♻️ Proposed change
 def load_yaml(filename):
-    with open(filename, "r") as f:
+    with open(filename, "r", encoding="utf-8") as f:
         return yaml.safe_load(f)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/core/messages.py` around lines 22 - 24, The load_yaml helper opens
translation YAML files without specifying an encoding, which can mis-decode
non-ASCII locales on platforms whose default encoding isn't UTF-8; update the
load_yaml function to open the file with encoding="utf-8" (i.e., change the
open(...) call in load_yaml to include encoding="utf-8") so translation files
under Config.path.locale_dir load deterministically across platforms.
nettacker/core/arg_parser.py (1)

739-743: This open(...) was missed by the context-manager refactor.

The stated goal of the commit is to fix file-handle leaks across arg_parser.py, but the output-file writability probe at Lines 739–743 still uses a bare open(..., "w") + close(). If open itself raises, the except path is fine, but if any future change between open and close raises, the handle leaks — which is exactly the class of bug the rest of this PR is eliminating. Worth finishing the job here for consistency.

Note also that open(..., "w") truncates the target file as a side effect of the "check" (pre-existing behavior); if you touch this anyway, consider os.access(..., os.W_OK) or opening in "a" mode instead so an existing report isn't wiped just because argument parsing ran.

♻️ Proposed change (minimal, preserves existing truncate-on-check behavior)
-        # Check output file
-        try:
-            temp_file = open(options.report_path_filename, "w")
-            temp_file.close()
-        except Exception:
-            die_failure(_("file_write_error").format(options.report_path_filename))
+        # Check output file
+        try:
+            with open(options.report_path_filename, "w"):
+                pass
+        except Exception:
+            die_failure(_("file_write_error").format(options.report_path_filename))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/core/arg_parser.py` around lines 739 - 743, The writability probe
currently uses open(options.report_path_filename, "w") followed by close(),
which can leak a file handle if an exception occurs between open and close;
change this to use a context manager (e.g., with
open(options.report_path_filename, "w") as _:) so the file is always closed, and
keep the existing truncate behavior, or alternatively use os.access(...,
os.W_OK) or open in "a" mode if you want to avoid truncation; ensure you still
call die_failure(_("file_write_error").format(options.report_path_filename)) on
exceptions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nettacker/database/db.py`:
- Around line 63-72: The whitelist for SQLite journal modes is missing
"TRUNCATE" and "PERSIST" and comparisons are case-sensitive; update
allowed_journal_modes to include "TRUNCATE" and "PERSIST", normalize
journal_mode and synchronous_mode (e.g., journal_mode =
Config.db.journal_mode.upper(), sync_mode = Config.db.synchronous_mode.upper())
before checking against allowed_journal_modes and allowed_sync_modes, and use
the normalized values in the cursor.execute PRAGMA calls
(cursor.execute(f"PRAGMA journal_mode={journal_mode}") and
cursor.execute(f"PRAGMA synchronous={sync_mode}")) so lower/upper-cased config
values are accepted.

In `@nettacker/database/mysql.py`:
- Around line 22-29: The try/except around the database-creation block is
swallowing the ValueError raised for invalid names (Config.db.name) and only
prints the exception; change this so validation errors propagate: either remove
the broad except or catch only operational exceptions and re-raise ValueError so
callers like mysql_create_tables() see the failure; also relax the db name check
(replace db_name.isalnum() and db_name[0].isalpha() logic) to allow underscores
(e.g. require the name match a pattern like starting with a letter then letters,
digits or underscores) and validate against existing_databases before executing
the CREATE DATABASE command.

In `@nettacker/database/postgresql.py`:
- Around line 29-33: The current validation rejects valid PostgreSQL names with
underscores; replace the isalnum()/isalpha() checks on db_name with a regex that
implements PostgreSQL unquoted-identifier grammar (e.g.
r'^[A-Za-z_][A-Za-z0-9_]*$') in nettacker/database/postgresql.py (and mirror the
same change in nettacker/database/mysql.py for the symmetric check), and when
raising the ValueError ensure you chain the original exception in the
surrounding error-handling (use "raise ValueError(... ) from err" where err is
the caught exception) so exception chaining requirements are satisfied.

---

Nitpick comments:
In `@nettacker/core/arg_parser.py`:
- Around line 739-743: The writability probe currently uses
open(options.report_path_filename, "w") followed by close(), which can leak a
file handle if an exception occurs between open and close; change this to use a
context manager (e.g., with open(options.report_path_filename, "w") as _:) so
the file is always closed, and keep the existing truncate behavior, or
alternatively use os.access(..., os.W_OK) or open in "a" mode if you want to
avoid truncation; ensure you still call
die_failure(_("file_write_error").format(options.report_path_filename)) on
exceptions.

In `@nettacker/core/messages.py`:
- Around line 22-24: The load_yaml helper opens translation YAML files without
specifying an encoding, which can mis-decode non-ASCII locales on platforms
whose default encoding isn't UTF-8; update the load_yaml function to open the
file with encoding="utf-8" (i.e., change the open(...) call in load_yaml to
include encoding="utf-8") so translation files under Config.path.locale_dir load
deterministically across platforms.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8d4d41c9-646b-4a84-9963-d7aed6c11678

📥 Commits

Reviewing files that changed from the base of the PR and between d873811 and f7b4bcc.

📒 Files selected for processing (5)
  • nettacker/core/arg_parser.py
  • nettacker/core/messages.py
  • nettacker/database/db.py
  • nettacker/database/mysql.py
  • nettacker/database/postgresql.py

Comment thread nettacker/database/db.py
Comment on lines +63 to +72
allowed_journal_modes = {"DELETE", "WAL", "MEMORY", "OFF"}
allowed_sync_modes = {"OFF", "NORMAL", "FULL", "EXTRA"}
journal_mode = Config.db.journal_mode
sync_mode = Config.db.synchronous_mode
if journal_mode not in allowed_journal_modes:
raise ValueError(f"Invalid journal_mode: {journal_mode}")
if sync_mode not in allowed_sync_modes:
raise ValueError(f"Invalid synchronous_mode: {sync_mode}")
cursor.execute(f"PRAGMA journal_mode={journal_mode}")
cursor.execute(f"PRAGMA synchronous={sync_mode}")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

SQLite PRAGMA journal_mode valid values list

💡 Result:

The valid values for SQLite PRAGMA journal_mode are: DELETE, TRUNCATE, PERSIST, MEMORY, WAL, OFF. These are explicitly listed in the official SQLite documentation as the options that can be queried or set using PRAGMA journal_mode = DELETE | TRUNCATE | PERSIST | MEMORY | WAL | OFF. DELETE is the default mode, using a rollback journal that is deleted after each transaction. TRUNCATE truncates the journal to zero length. PERSIST overwrites the journal header but keeps the file. MEMORY stores the journal in RAM (risking corruption on crash). WAL uses write-ahead logging for better concurrency. OFF disables journaling entirely (highly risky, disables atomic commits). Note that for in-memory databases, only MEMORY or OFF are applicable, and changes to other modes are ignored. WAL mode is persistent across connections.

Citations:


🏁 Script executed:

# First, locate and read the database file
find . -name "db.py" -path "*/database/*" | head -20

Repository: OWASP/Nettacker

Length of output: 86


🏁 Script executed:

# Read the specific lines from the file
sed -n '50,80p' ./nettacker/database/db.py

Repository: OWASP/Nettacker

Length of output: 1464


🌐 Web query:

SQLite PRAGMA synchronous valid values documentation

💡 Result:

The valid values for the SQLite PRAGMA synchronous are: - 0 or OFF: Minimal synchronization, fastest but risks corruption on OS crash or power loss. - 1 or NORMAL: Syncs at critical moments, good balance for WAL mode, small risk in rollback mode. - 2 or FULL: Ensures data is written to disk before continuing, safe against crashes (default for rollback journal). - 3 or EXTRA: Like FULL but also syncs the directory after unlinking rollback journal, maximum durability. Usage: PRAGMA [schema.]synchronous; (query) or PRAGMA [schema.]synchronous = value; (set). TEMP schema always uses OFF. FULL is recommended for durability in rollback mode; NORMAL for WAL mode performance.

Citations:


Whitelist omits two valid SQLite journal modes and uses case-sensitive comparison.

SQLite's PRAGMA journal_mode accepts DELETE | TRUNCATE | PERSIST | MEMORY | WAL | OFF (case-insensitive). The current whitelist omits TRUNCATE and PERSIST, preventing users with those modes configured from initializing connections. Additionally, the case-sensitive comparison means configs like "wal" or "truncate" will be rejected despite SQLite accepting them. The synchronous_mode whitelist is already complete.

🔧 Proposed fix
-            allowed_journal_modes = {"DELETE", "WAL", "MEMORY", "OFF"}
-            allowed_sync_modes = {"OFF", "NORMAL", "FULL", "EXTRA"}
-            journal_mode = Config.db.journal_mode
-            sync_mode = Config.db.synchronous_mode
-            if journal_mode not in allowed_journal_modes:
-                raise ValueError(f"Invalid journal_mode: {journal_mode}")
-            if sync_mode not in allowed_sync_modes:
-                raise ValueError(f"Invalid synchronous_mode: {sync_mode}")
-            cursor.execute(f"PRAGMA journal_mode={journal_mode}")
-            cursor.execute(f"PRAGMA synchronous={sync_mode}")
+            allowed_journal_modes = {"DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF"}
+            allowed_sync_modes = {"OFF", "NORMAL", "FULL", "EXTRA"}
+            journal_mode = str(Config.db.journal_mode).upper()
+            sync_mode = str(Config.db.synchronous_mode).upper()
+            if journal_mode not in allowed_journal_modes:
+                raise ValueError(f"Invalid journal_mode: {Config.db.journal_mode}")
+            if sync_mode not in allowed_sync_modes:
+                raise ValueError(f"Invalid synchronous_mode: {Config.db.synchronous_mode}")
+            cursor.execute(f"PRAGMA journal_mode={journal_mode}")
+            cursor.execute(f"PRAGMA synchronous={sync_mode}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
allowed_journal_modes = {"DELETE", "WAL", "MEMORY", "OFF"}
allowed_sync_modes = {"OFF", "NORMAL", "FULL", "EXTRA"}
journal_mode = Config.db.journal_mode
sync_mode = Config.db.synchronous_mode
if journal_mode not in allowed_journal_modes:
raise ValueError(f"Invalid journal_mode: {journal_mode}")
if sync_mode not in allowed_sync_modes:
raise ValueError(f"Invalid synchronous_mode: {sync_mode}")
cursor.execute(f"PRAGMA journal_mode={journal_mode}")
cursor.execute(f"PRAGMA synchronous={sync_mode}")
allowed_journal_modes = {"DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF"}
allowed_sync_modes = {"OFF", "NORMAL", "FULL", "EXTRA"}
journal_mode = str(Config.db.journal_mode).upper()
sync_mode = str(Config.db.synchronous_mode).upper()
if journal_mode not in allowed_journal_modes:
raise ValueError(f"Invalid journal_mode: {Config.db.journal_mode}")
if sync_mode not in allowed_sync_modes:
raise ValueError(f"Invalid synchronous_mode: {Config.db.synchronous_mode}")
cursor.execute(f"PRAGMA journal_mode={journal_mode}")
cursor.execute(f"PRAGMA synchronous={sync_mode}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/database/db.py` around lines 63 - 72, The whitelist for SQLite
journal modes is missing "TRUNCATE" and "PERSIST" and comparisons are
case-sensitive; update allowed_journal_modes to include "TRUNCATE" and
"PERSIST", normalize journal_mode and synchronous_mode (e.g., journal_mode =
Config.db.journal_mode.upper(), sync_mode = Config.db.synchronous_mode.upper())
before checking against allowed_journal_modes and allowed_sync_modes, and use
the normalized values in the cursor.execute PRAGMA calls
(cursor.execute(f"PRAGMA journal_mode={journal_mode}") and
cursor.execute(f"PRAGMA synchronous={sync_mode}")) so lower/upper-cased config
values are accepted.

Comment on lines 22 to 29
if Config.db.name not in existing_databases:
conn.execute(text("CREATE DATABASE {0} ".format(Config.db.name)))
db_name = Config.db.name
if db_name.isalnum() and db_name[0].isalpha():
conn.execute(text(f"CREATE DATABASE `{db_name}` "))
else:
raise ValueError(f"Invalid database name: {db_name}")
except Exception as e:
print(e)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The raised ValueError is silently swallowed by the surrounding except Exception: print(e).

The new validation raises ValueError (line 27), but the outer handler at lines 28–29 just prints it and returns normally. Execution continues to mysql_create_tables() in the caller, which will then fail with a generic connection error — the security signal is lost and the failure mode becomes confusing. Either re-raise from the handler or narrow the except so the validation error propagates.

🔧 Proposed fix
-    except Exception as e:
-        print(e)
+    except ValueError:
+        raise
+    except Exception as e:
+        print(e)

Also, db_name.isalnum() rejects underscores, which are common in valid MySQL database names — see the note on nettacker/database/postgresql.py (lines 29–33) for the suggested relaxed check; the same should apply here at line 24.

🧰 Tools
🪛 Ruff (0.15.11)

[warning] 28-28: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/database/mysql.py` around lines 22 - 29, The try/except around the
database-creation block is swallowing the ValueError raised for invalid names
(Config.db.name) and only prints the exception; change this so validation errors
propagate: either remove the broad except or catch only operational exceptions
and re-raise ValueError so callers like mysql_create_tables() see the failure;
also relax the db name check (replace db_name.isalnum() and db_name[0].isalpha()
logic) to allow underscores (e.g. require the name match a pattern like starting
with a letter then letters, digits or underscores) and validate against
existing_databases before executing the CREATE DATABASE command.

Comment on lines +29 to +33
db_name = Config.db.name
if db_name.isalnum() and db_name[0].isalpha():
conn.execute(text(f'CREATE DATABASE "{db_name}"'))
else:
raise ValueError(f"Invalid database name: {db_name}")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validation is too strict — rejects valid PostgreSQL identifiers containing underscores.

db_name.isalnum() returns False for any name containing _ (e.g., nettacker_db, owasp_nettacker). Underscores are standard and extremely common in PostgreSQL database names, so this change can break existing deployments the first time the OperationalError recovery path runs. Consider matching the PostgreSQL unquoted-identifier grammar instead, and chain the exception to satisfy B904.

🔧 Proposed fix
+import re
 ...
         db_name = Config.db.name
-        if db_name.isalnum() and db_name[0].isalpha():
+        if re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", db_name):
             conn.execute(text(f'CREATE DATABASE "{db_name}"'))
         else:
-            raise ValueError(f"Invalid database name: {db_name}")
+            raise ValueError(f"Invalid database name: {db_name}") from None

The same tightening applies symmetrically in nettacker/database/mysql.py (line 24).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
db_name = Config.db.name
if db_name.isalnum() and db_name[0].isalpha():
conn.execute(text(f'CREATE DATABASE "{db_name}"'))
else:
raise ValueError(f"Invalid database name: {db_name}")
import re
db_name = Config.db.name
if re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", db_name):
conn.execute(text(f'CREATE DATABASE "{db_name}"'))
else:
raise ValueError(f"Invalid database name: {db_name}") from None
🧰 Tools
🪛 Ruff (0.15.11)

[warning] 33-33: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/database/postgresql.py` around lines 29 - 33, The current
validation rejects valid PostgreSQL names with underscores; replace the
isalnum()/isalpha() checks on db_name with a regex that implements PostgreSQL
unquoted-identifier grammar (e.g. r'^[A-Za-z_][A-Za-z0-9_]*$') in
nettacker/database/postgresql.py (and mirror the same change in
nettacker/database/mysql.py for the symmetric check), and when raising the
ValueError ensure you chain the original exception in the surrounding
error-handling (use "raise ValueError(... ) from err" where err is the caught
exception) so exception chaining requirements are satisfied.

@pUrGe12
Copy link
Copy Markdown
Contributor

pUrGe12 commented Apr 24, 2026

Duplicate of #1527.

@securestep9
Copy link
Copy Markdown
Collaborator

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
nettacker/database/postgresql.py (1)

29-34: ⚠️ Potential issue | 🟠 Major

Keep the fallback valid and leak-free.

On Line 29, isalnum() still rejects standard names like nettacker_db, and on Line 33 the new ValueError bypasses conn.close(). Please switch to an identifier regex that matches the names you actually support, close the autocommit connection in a finally, and raise the validation error with from None so the traceback isn't reported as another OperationalError.

🔧 Suggested fix
+import re
 ...
-        conn = engine.connect()
-        conn = conn.execution_options(isolation_level="AUTOCOMMIT")
-        db_name = Config.db.name
-        if db_name.isalnum() and db_name[0].isalpha():
-            conn.execute(text(f'CREATE DATABASE "{db_name}"'))
-        else:
-            raise ValueError(f"Invalid database name: {db_name}")
-        conn.close()
+        conn = engine.connect()
+        conn = conn.execution_options(isolation_level="AUTOCOMMIT")
+        try:
+            db_name = Config.db.name
+            if re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", db_name):
+                conn.execute(text(f'CREATE DATABASE "{db_name}"'))
+            else:
+                raise ValueError(f"Invalid database name: {db_name}") from None
+        finally:
+            conn.close()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/database/postgresql.py` around lines 29 - 34, The validation for
Config.db.name in the database creation block is too strict and the connection
may leak on error; replace the isalnum()/isalpha() checks with a regex like
r'^[A-Za-z][A-Za-z0-9_]*$' to allow underscores and standard identifiers, wrap
the conn.execute(text(...)) and any validation in a try/finally that calls
conn.close() in the finally to ensure the autocommit connection is always
closed, and when raising the validation error use raise ValueError(f"Invalid
database name: {db_name}") from None so the traceback doesn't surface as an
OperationalError; adjust the code around db_name, conn.execute, and conn.close()
accordingly.
nettacker/database/mysql.py (1)

23-29: ⚠️ Potential issue | 🟠 Major

Let invalid names fail fast instead of being printed and ignored.

On Line 23, the name check still rejects underscores, and on Line 28 the broad except Exception prints and suppresses the new ValueError. Re-raise validation errors so startup fails fast when the configured database name is invalid.

🔧 Suggested fix
+import re
 ...
-            if db_name.isalnum() and db_name[0].isalpha():
+            if re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", db_name):
                 conn.execute(text(f"CREATE DATABASE `{db_name}` "))
             else:
-                raise ValueError(f"Invalid database name: {db_name}")
-    except Exception as e:
-        print(e)
+                raise ValueError(f"Invalid database name: {db_name}") from None
+    except ValueError:
+        raise
+    except Exception as e:
+        print(e)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/database/mysql.py` around lines 23 - 29, The database-name
validation currently rejects underscores and then a broad except prints and
suppresses the ValueError; update the validation for Config.db.name (db_name) to
allow names starting with a letter and containing letters, digits or underscores
(e.g. use a regex like ^[A-Za-z][A-Za-z0-9_]*$) and when validation fails raise
the ValueError (do not swallow it). Also remove or narrow the broad except
Exception block around the CREATE DATABASE/validation so that ValueError is not
caught and suppressed (if you must catch exceptions, log them and re-raise
rather than only printing).
nettacker/database/db.py (1)

63-72: ⚠️ Potential issue | 🟠 Major

Expand the SQLite allowlist and normalize case before comparing.

On Line 63, the journal-mode whitelist still omits TRUNCATE and PERSIST, and the direct string comparison rejects lowercase config values even though SQLite treats these PRAGMAs case-insensitively. Normalize both values before validating/executing them, and make sure the APSW handles are closed if validation fails.

🔧 Suggested fix
-            allowed_journal_modes = {"DELETE", "WAL", "MEMORY", "OFF"}
+            allowed_journal_modes = {"DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF"}
             allowed_sync_modes = {"OFF", "NORMAL", "FULL", "EXTRA"}
-            journal_mode = Config.db.journal_mode
-            sync_mode = Config.db.synchronous_mode
+            journal_mode = str(Config.db.journal_mode).upper()
+            sync_mode = str(Config.db.synchronous_mode).upper()
             if journal_mode not in allowed_journal_modes:
-                raise ValueError(f"Invalid journal_mode: {journal_mode}")
+                raise ValueError(f"Invalid journal_mode: {Config.db.journal_mode}")
             if sync_mode not in allowed_sync_modes:
-                raise ValueError(f"Invalid synchronous_mode: {sync_mode}")
+                raise ValueError(f"Invalid synchronous_mode: {Config.db.synchronous_mode}")
             cursor.execute(f"PRAGMA journal_mode={journal_mode}")
             cursor.execute(f"PRAGMA synchronous={sync_mode}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/database/db.py` around lines 63 - 72, The journal/sync allowlists
need TRUNCATE and PERSIST added and comparisons should be case-normalized:
update allowed_journal_modes to include "TRUNCATE" and "PERSIST", convert
Config.db.journal_mode and Config.db.synchronous_mode to a normalized form (e.g.
.upper()) before validating and use the normalized value when calling
cursor.execute(f"PRAGMA journal_mode={...}") and cursor.execute(f"PRAGMA
synchronous={...}"); additionally, if validation fails make sure to close any
APSW handles (e.g. cursor and the connection object) before raising the
ValueError to avoid leaking resources.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nettacker/core/arg_parser.py`:
- Around line 615-618: The current broad except Exception around the file-read
blocks (e.g., the targets_list read that sets options.targets) swallows decode
and unrelated errors; narrow the catch to the specific file and decode errors
you expect (FileNotFoundError, PermissionError, OSError and UnicodeDecodeError)
and handle them the same way you currently call
die_failure(_("error_target_file").format(...)); apply this tightening to all
four file-read sites (targets_list, passwords_list, and the two read_from_file
places referenced) so only genuine file I/O or decode failures are caught while
other exceptions propagate.

---

Duplicate comments:
In `@nettacker/database/db.py`:
- Around line 63-72: The journal/sync allowlists need TRUNCATE and PERSIST added
and comparisons should be case-normalized: update allowed_journal_modes to
include "TRUNCATE" and "PERSIST", convert Config.db.journal_mode and
Config.db.synchronous_mode to a normalized form (e.g. .upper()) before
validating and use the normalized value when calling cursor.execute(f"PRAGMA
journal_mode={...}") and cursor.execute(f"PRAGMA synchronous={...}");
additionally, if validation fails make sure to close any APSW handles (e.g.
cursor and the connection object) before raising the ValueError to avoid leaking
resources.

In `@nettacker/database/mysql.py`:
- Around line 23-29: The database-name validation currently rejects underscores
and then a broad except prints and suppresses the ValueError; update the
validation for Config.db.name (db_name) to allow names starting with a letter
and containing letters, digits or underscores (e.g. use a regex like
^[A-Za-z][A-Za-z0-9_]*$) and when validation fails raise the ValueError (do not
swallow it). Also remove or narrow the broad except Exception block around the
CREATE DATABASE/validation so that ValueError is not caught and suppressed (if
you must catch exceptions, log them and re-raise rather than only printing).

In `@nettacker/database/postgresql.py`:
- Around line 29-34: The validation for Config.db.name in the database creation
block is too strict and the connection may leak on error; replace the
isalnum()/isalpha() checks with a regex like r'^[A-Za-z][A-Za-z0-9_]*$' to allow
underscores and standard identifiers, wrap the conn.execute(text(...)) and any
validation in a try/finally that calls conn.close() in the finally to ensure the
autocommit connection is always closed, and when raising the validation error
use raise ValueError(f"Invalid database name: {db_name}") from None so the
traceback doesn't surface as an OperationalError; adjust the code around
db_name, conn.execute, and conn.close() accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9bce497a-48d2-4ad3-ae2e-413e1caf1e9b

📥 Commits

Reviewing files that changed from the base of the PR and between d873811 and f7b4bcc.

📒 Files selected for processing (5)
  • nettacker/core/arg_parser.py
  • nettacker/core/messages.py
  • nettacker/database/db.py
  • nettacker/database/mysql.py
  • nettacker/database/postgresql.py

Comment on lines +615 to 618
with open(options.targets_list, "rb") as f:
options.targets = list(set(f.read().decode().split()))
except Exception:
die_failure(_("error_target_file").format(options.targets_list))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Tighten the file-read exception scope.

The new with blocks are good, but these catches still swallow decode and I/O failures under a generic Exception, which makes bad input look like the same die_failure(...) path. Narrow them to the file errors you actually expect and apply the same change to all four read-from-file paths.

♻️ Example tightening
-            except Exception:
+            except (OSError, UnicodeDecodeError):
                 die_failure(_("error_username").format(options.usernames_list))

Apply the same narrowing to the targets_list, passwords_list, and read_from_file blocks.

Also applies to: 717-721, 726-730, 733-737

🧰 Tools
🪛 Ruff (0.15.12)

[warning] 617-617: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/core/arg_parser.py` around lines 615 - 618, The current broad
except Exception around the file-read blocks (e.g., the targets_list read that
sets options.targets) swallows decode and unrelated errors; narrow the catch to
the specific file and decode errors you expect (FileNotFoundError,
PermissionError, OSError and UnicodeDecodeError) and handle them the same way
you currently call die_failure(_("error_target_file").format(...)); apply this
tightening to all four file-read sites (targets_list, passwords_list, and the
two read_from_file places referenced) so only genuine file I/O or decode
failures are caught while other exceptions propagate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.