urlformat-audit
Check a labelled phishing / malicious URL dataset for format shortcuts: URL formatting
(scheme, www., trailing slash, path) that differs between the classes and lets a model
predict the label without learning anything about the threat.
Example: in some datasets every legitimate URL is written https://www.<host> and phishing
URLs are not. A model trained on such data learns "legitimate = this template". It scores
well in-dataset and fails on any other dataset.
Author Details
Md Istiak Tanvir - eruddro@gmail.com Msc in CSE - MIST Dhaka, Bangladesh
urlformat-audit reports:
| Output | Meaning |
|---|---|
| Class table | % of URLs per class with each format property: https, http, no scheme, www., trailing slash, path, query, port, IP host, uppercase, median length |
| Format cells | the 24 cells scheme (https/http/none) × www. × trailing slash × path, with class counts and purity |
| Format-only rule | predicts the majority label of each cell. Its MCC says how much of the label format alone explains. MCC ≥ 0.5 is flagged. |
| Template rule | "legitimate iff the URL matches ^https://www\.[a-z0-9.-]+$" |
| Trivial baseline | predict the positive class everywhere |
| NMI | I(cell; label) / H(label): share of label entropy explained by the format cell (0 = none, 1 = all) |
| Flags | properties whose prevalence differs by ≥ 30 percentage points between the classes, and large single-class cells |
| compare | fit the format-only rule on dataset A, evaluate it on dataset B. This shows whether format cues transfer, fail, or invert. |
| factorial | writes the 24 format variants of every URL, for testing a trained model's sensitivity to formatting |
Requires Python ≥ 3.9 with numpy and pandas. Nothing else.
Install
pip install urlformat-audit
Command line
# one file with a label column (the positive class is the phishing/malicious label)
urlformat-audit audit data.csv --positive phishing
urlformat-audit audit data.csv --url-col URL --label-col label --positive 1 --json report.json
# one file per class, without a label column (CSV/TSV with a url column, or a JSON list of URLs)
urlformat-audit audit legit.json phish.json --label 0 1 --name "my dataset"
# does a format-only rule fitted on A transfer to B?
urlformat-audit compare A.csv B.csv --positive 1
# 24 format variants of every URL (columns: id = row of the input, cell, url_variant)
urlformat-audit factorial test.csv -o test_factorial.csv
Example output (dataset of Sahingoz et al., 2019; legitimate = 0, phishing = 1), abridged:
Format-only rule (majority per cell): accuracy=0.756 balanced_accuracy=0.756 f1_positive=0.771 mcc=0.515 tpr=0.812 fpr=0.300
Template rule (legit iff ^https://www.<host>$): accuracy=0.505 ... mcc=0.000
Normalized mutual information I(cell;label)/H(label) = 0.266
Shortcut flags:
- scheme=https: 36.8% of negatives vs 1.8% of positives
- scheme=http: 63.2% of negatives vs 97.7% of positives
- format-only rule (4 cues, 24 cells) reaches MCC 0.515 / balanced accuracy 0.756 on this dataset
Python API
from urlformat_audit import audit, compare, factorial_variants, format_features, FormatRule
r = audit(urls, labels, name="my data") # labels: 1 = phishing/malicious, 0 = legitimate
print(r.format_rule["mcc"], r.nmi_cell_label)
print(r.class_table) # pandas DataFrame
print(r.cell_table.head())
for f in r.flags: print(f)
r.to_dict() # JSON-serialisable
res = compare(train_urls, train_labels, test_urls, test_labels)
res["format_rule_transfer"]["mcc"], res["share_target_in_unseen_cells"]
factorial_variants("http://example.org/login/") # [(cell_name, url), ...] x 24
format_features(urls) # per-URL format columns + cell
How to read the result
- Format-only rule MCC near 1 (or a NMI near 1): format alone separates the classes, so any model that sees the raw URL can learn the shortcut. Cross-dataset results on this data need format-controlled checks: factorial edits, host-only input, or a scheme-neutral representation.
- High in-dataset MCC but low or negative
compareMCC: the format cue is specific to that dataset. Expect transfer failures. - Low MCC: format is not a major shortcut. This says nothing about other artifacts (domains, TLDs, collection time).
The audit is descriptive. It cannot tell you whether a format difference is a collection artifact or a real property of phishing URLs; it only tells you that a model can use it.
Development
pip install -e ".[test]"
pytest
python -m build # sdist + wheel in dist/
Citation
If you use this tool, please cite the accompanying paper: Learning the Template, Not the Threat: Format Shortcuts in Cross-Dataset Phishing URL Detection (authors and venue to be added).
License
MIT
Metadata
Release files for urlformat-audit 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| urlformat_audit-0.1.0.tar.gz | 11.0 kB | Without provenance | Details for urlformat_audit-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| urlformat_audit-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for urlformat_audit-0.1.0-py3-none-any.whl |
Total release size: 24.5 kB
Release files / urlformat_audit-0.1.0.tar.gz
| Download URL | urlformat_audit-0.1.0.tar.gz |
|---|---|
| Size | 11.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
60e9e7dcef1acf60682ea1658e32c53867b64648e2d17d7196d776eaed8d5389
|
|
BLAKE2b-256 checksum How to use checksums |
93743cb042ddf7361fd6980cb8f053d24d8478ed8dde714f931a7eb1910518fc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.2
|
Release files / urlformat_audit-0.1.0-py3-none-any.whl
| Download URL | urlformat_audit-0.1.0-py3-none-any.whl |
|---|---|
| Size | 13.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3f4085507a420251676d42bd31cd6a3c8dfd0e5346bbaa851aad844f266ca6d7
|
|
BLAKE2b-256 checksum How to use checksums |
fde49f0921636b7366a6611d6fc5be87e2b1ef3e8033478d91bde84a65d08c3e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.2
|
Security for urlformat-audit 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.