Sherlock solves the practical problem of determining whether a given username exists across hundreds of social networks. Manual checking is slow and error-prone; this tool automates the process by sending targeted HTTP requests to each platform and interpreting the responses.
What This Does
The repository is a fork of the popular sherlock-project/sherlock, providing a command-line tool that checks username availability across 400+ social networks. The core logic lives in the sherlock_project/ package, with supporting test infrastructure and Docker/Apify actor configurations for deployment.
The tool accepts one or more usernames, iterates through a site manifest (sherlock_project/resources/data.json), and reports which platforms have an account matching the username. It supports output to text, CSV, and XLSX formats, plus optional proxy support.
How It Is Wired
Execution starts at main in sherlock_project/sherlock.py:538. It reaches 13 functions and is called from one place (sherlock_project/__main__.py). The flow:
main calls SitesInformation to load the site list, then remove_nsfw_sites to filter unless --nsfw is passed.
For each username, main calls sherlock, which orchestrates the checking. sherlock is called from 4 places and is the central hub.
sherlock calls interpolate_string (3 times) to expand wildcards, then request for each site. request makes the actual outbound network call via requests.get.
Results are collected in QueryResult objects and reported through QueryNotify (notify.py), which handles file output and terminal display.
The module graph shows sherlock_project/sherlock with instability 0.67 (Ca 2, Ce 4) - it both depends on and is depended upon, making it the change hotspot. sherlock_project/__init__.py has Ca 5 but Ce 0, so it's a leaf that many import but it imports nothing itself.
The traced network path is short: main → sherlock → request → requests.get. File output goes through notify.py, which also handles the cryptographic operation (likely for the Apify actor's secret handling).
How To Use It
Setup: Install with pipx install sherlock-project or run the Docker image.
Running it:
sherlock user123
sherlock user1 user2 user3
Results save to individual text files per username. Use --csv or --xlsx for structured output, --site to limit to specific platforms, --proxy for proxied requests.
Real-World Use
A security researcher validating a username across platforms before an engagement:
sherlock --csv --output findings.csv --site github --site twitter target_user
This produces a CSV with confirmed accounts, which can feed into a broader OSINT pipeline.
Code Health & Issues
Static analysis found 12 issues (4 high, 8 medium) across 6 kinds:
High - Deep nesting: result.py, notify.py, sherlock.py have max indentation depth 10; control flow is hard to follow. Fix with early returns and guard clauses.
Medium - File opened without context manager: notify.py has open() calls not wrapped in with.
Medium - Broad exception handling: sherlock.py and sites.py catch Exception indiscriminately.
Medium - Oversized file: sherlock.py at 707 lines is a change hotspot.
Medium - Duplicated code: 2 repeated 6-line blocks across notify.py, result.py, sites.py.
Medium - High branching density: devel/summarize_site_validation.py and tests/test_validate_targets.py have 15 branch points over 51 lines.
SDLC observations: CI exists via GitHub Actions, tests are present, and a Dockerfile is included. However, third-party actions are pinned to mutable tags (not commit SHAs), and CI has continue-on-error on a correctness step in validate_modified_targets.yml. Dependencies lack a lockfile, and the container base image uses a mutable tag.
The Bottom Line
This is a functional, well-tested tool for a narrow purpose. The code works but has maintainability debt in sherlock.py and the notification layer. The CI/CD hygiene needs tightening before production use. It's suitable for security researchers and OSINT practitioners; the Docker image makes deployment straightforward.
What the analyser found
Deployment readiness
6/7
✓Container image
✓CI pipeline
✗Lockfile committed
✓Test suite
✓README
✓License
✓No committed secrets
Composition
52 files
Python16
YAML11
Markdown6
JSON5
Shell1
TOML1
Docker
Architecture
Top-level areas of the codebase, sized by module count. Arrows show how many imports cross from one area into another.
Ranked by severity × confidence × production reach. Reach is the honest discriminator across a collection that is mostly other people's code: the same finding matters more in something that ships.
high4
medium7
low1
Pin third-party GitHub Actions to a commit SHAhigh3 occurrences
A tag can be moved, so the action running with your token and secrets is whatever its owner last pushed; this is how tj-actions/changed-files leaked secrets from thousands of repos.
Fix: Replace each @vN with the 40-character commit SHA, keep # vN as a comment, and let Dependabot bump the SHAs.
Commit a lockfile beside the manifesthigh
pyproject.toml
manifest with no lockfile in scope
An unlocked range means the artifact you tested and the artifact you ship can contain different transitive code, so a malicious patch release reaches production with no diff.
Fix: Run the package manager once and commit the generated lockfile.
Remove continue-on-error from steps that gate correctnesshigh
.github/workflows/validate_modified_targets.yml
line 94 on a correctness step
A failing test suite reports a green check, which is precisely the path this audit exists to close.
Fix: Delete continue-on-error, or move a genuinely advisory step into a separate non-required job.
Open a pull request instead of pushing to the default branchhigh
.github/workflows/exclusions.yml
git push origin exclusions
Automated commits land on the branch that deploys, with no test having run against the result.
Fix: Push to a bot branch and open a pull request, or restrict the push to a tag ref.
Declare least-privilege permissions for GITHUB_TOKENmedium3 occurrences
.github/workflows/exclusions.yml
3 workflow(s) declare no permissions, 1 of them reference secrets
With no declaration the token inherits the repository default, so any injected step can push commits or mint releases from inside your own CI.
Fix: Add permissions: contents: read at the top of the workflow and widen per job only where needed.
Enable Dependabot or Renovatemedium
1 manifest(s), no update bot configured
Without a bot a published advisory sits unpatched until someone audits by hand, which across 1,322 repositories means never.
Fix: Commit .github/dependabot.yml covering the repo ecosystems plus github-actions.
Pin the container base image by digestmedium
.actor/Dockerfile
sherlock/sherlock (mutable tag)
An untagged or mutable base means today's build and last month's contain different libc and a different CVE set, with no record of which shipped.
Fix: Use image:tag@sha256:<digest> and enable Dependabot's docker ecosystem.
Gate pull requests on a dependency vulnerability scanmedium
.github/workflows
no dependency scan in CI
This is the one gate that would catch a known-vulnerable package before it reaches a build, and no repository in the sample had it.
Fix: Add dependency-review-action on pull_request, or osv-scanner on push and a schedule.
Set persist-credentials: false on checkoutmedium
.github/workflows/regression.yml
checkout keeps the token, then dependencies are installed
The token stays in .git/config for every later step, so a malicious postinstall script reads a pushable credential without one ever being passed to it.
Fix: Add with: persist-credentials: false, and pass an explicit token only to the step that pushes.
Add a non-root USER to the imagemedium
.actor/Dockerfile
CMD or ENTRYPOINT with no USER directive
A process running as root in the container is root against every mounted volume, and it turns any container escape or writable-mount mistake from a contained problem into a host one.
Fix: Create an unprivileged user, chown what it needs, and end the Dockerfile with USER.
Pin the resolution so a vulnerable version cannot installmedium13 occurrences
no lockfile, and the declared ranges reach certifi@2019.6.16 CVE-2023-37920 (high) +4 more; requests@2.22.0 CVE-2024-47081 (high) +7 more
The range includes versions with published advisories and nothing in the repository records which one was resolved, so two installs a month apart can differ in whether they are vulnerable and neither is reproducible from what is committed.
Fix: Commit the lockfile, which fixes the resolution and makes this answerable.
Set timeout-minutes on the workflow jobslow4 occurrences
.github/workflows/exclusions.yml
4 workflow(s) declare no job timeout
A wedged step runs to the six-hour platform default, which on a two-hourly schedule means three runs overlap behind it.
Fix: Add timeout-minutes with a realistic bound to each job.
Checked deterministically against the repository tree and a bounded set of its files: committed credentials, unpinned actions and base images, missing lockfiles and update bots, workflows that discard failures, published advisories against the declared dependencies, runtime configuration, licensing and notebook reproducibility. No language model is involved in this section.