Skip to content

feat: plaintext credentials - #58

Merged
SizzleUnrlsd merged 2 commits into
mainfrom
feat/plaintext-credentials
Sep 4, 2026
Merged

SizzleUnrlsd merged 2 commits into
mainfrom
feat/plaintext-credentials

Conversation

@SizzleUnrlsd

Copy link
Copy Markdown
Contributor

Summary

The last actionable point of the manual audit under tests-project/: passwords stored in a database without hashing (uploadbox/db.py:24).

  • Tests first (test: define plaintext credential storage), implementation follows.
  • TaintKind.CREDENTIAL, outside TaintKind.ALL on purpose: only credential-named parameters carry it, so ordinary input reaching a database write is a SQL injection question, never a plaintext credential one.
  • NamedParameter model: parameters whose name matches a pattern are sources of the given kinds, wherever the function is; the shipped models/credentials plugin names password, passwd, pwd and their variants as credential sources and declares the password hashing functions of hashlib, bcrypt, argon2, werkzeug, passlib and Django as sanitisers of that kind. The database write sinks of the stdlib, SQLAlchemy and Django models carry SQL | CREDENTIAL, so no plugin has to re-register another plugin's sink.
  • security/plaintext_credentials: a TaintDetector for the CREDENTIAL kind at medium confidence by construction, a name being a hint; TaintDetector gains a confidence class attribute and lowers hotspots one step from it.
  • The audit's own case stores through get_db().execute(...), a connection returned by a project function: the taint engine now resolves a method called on what a known function returns through that function's summary, when it returns one external symbol.

On the twelve repositories this adds two findings, both in uploadbox/db.py: the registration insert at line 24, the line the audit cites, and the login query at line 31 that compares the password in clear, which the audit lists under the same point since comparing in clear implies storing in clear. A getter returning several possible symbols (getattr(g, '_database', None) or a fresh connection) resolves to the one the models know. Of the audit's seven recommendations, only private data served from static is left, a Flask-specific deployment rule deliberately not modelled.

@SizzleUnrlsd SizzleUnrlsd self-assigned this Sep 4, 2026
@SizzleUnrlsd
SizzleUnrlsd merged commit ba9d68a into main Sep 4, 2026
6 checks passed
@SizzleUnrlsd
SizzleUnrlsd deleted the feat/plaintext-credentials branch September 4, 2026 12:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant