security: add whitelist validation for SQLite PRAGMA settings#1529
security: add whitelist validation for SQLite PRAGMA settings#1529mitre88 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
…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.
Summary by CodeRabbit
WalkthroughArgument-file reading now uses context managers; YAML loading uses Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 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(): |
There was a problem hiding this comment.
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(): |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
nettacker/core/messages.py (1)
22-24: Consider specifyingencoding="utf-8"when loading language YAML files.
load_yamlis used primarily to load translation files underConfig.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: Thisopen(...)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 bareopen(..., "w")+close(). Ifopenitself raises, theexceptpath is fine, but if any future change betweenopenandcloseraises, 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, consideros.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
📒 Files selected for processing (5)
nettacker/core/arg_parser.pynettacker/core/messages.pynettacker/database/db.pynettacker/database/mysql.pynettacker/database/postgresql.py
| 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}") |
There was a problem hiding this comment.
🧩 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:
- 1: https://sqlite.org/pragma.html
- 2: https://sqlite.org/search?q=journal_mode
- 3: https://sqlite.org/isolation.html
- 4: https://www.sqlite.org/wal.html
🏁 Script executed:
# First, locate and read the database file
find . -name "db.py" -path "*/database/*" | head -20Repository: OWASP/Nettacker
Length of output: 86
🏁 Script executed:
# Read the specific lines from the file
sed -n '50,80p' ./nettacker/database/db.pyRepository: 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:
- 1: https://sqlite.org/pragma.html
- 2: https://www.typeerror.org/docs/sqlite/pragma
- 3: https://system.data.sqlite.org/home/doc/914417fc18aae0fb/Doc/Extra/Core/pragma.html
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.
| 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.
| 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) |
There was a problem hiding this comment.
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.
| 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}") |
There was a problem hiding this comment.
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 NoneThe 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.
| 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.
|
Duplicate of #1527. |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
nettacker/database/postgresql.py (1)
29-34:⚠️ Potential issue | 🟠 MajorKeep the fallback valid and leak-free.
On Line 29,
isalnum()still rejects standard names likenettacker_db, and on Line 33 the newValueErrorbypassesconn.close(). Please switch to an identifier regex that matches the names you actually support, close the autocommit connection in afinally, and raise the validation error withfrom Noneso the traceback isn't reported as anotherOperationalError.🔧 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 | 🟠 MajorLet 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 Exceptionprints and suppresses the newValueError. 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 | 🟠 MajorExpand the SQLite allowlist and normalize case before comparing.
On Line 63, the journal-mode whitelist still omits
TRUNCATEandPERSIST, 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
📒 Files selected for processing (5)
nettacker/core/arg_parser.pynettacker/core/messages.pynettacker/database/db.pynettacker/database/mysql.pynettacker/database/postgresql.py
| 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)) |
There was a problem hiding this comment.
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.
Summary
Add whitelist validation for SQLite PRAGMA settings to prevent potential SQL injection.
Changes
nettacker/database/db.py: Addallowed_journal_modesandallowed_sync_modeswhitelistsConfig.db.journal_modeandConfig.db.synchronous_modeagainst whitelistsValueErrorif invalid values are providedWhy
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.