DEV Community

Cover image for DevGuard: A Zero-Dependency Security Scanner Built from Python's Standard Library
MRUDULA SHISHUPAL
MRUDULA SHISHUPAL

Posted on

DevGuard: A Zero-Dependency Security Scanner Built from Python's Standard Library

What happens when you remove the usual security libraries and ask: can we still build a useful developer security tool?

For Zero Dependency 2026, we built DevGuard — a lightweight security scanner for codebases and dependency manifests using Python's standard library.

The constraint was simple:

Build something useful without relying on third-party runtime packages.

That constraint ended up influencing almost every design decision we made.

Why We Built DevGuard

Developers can accidentally expose credentials through source files, environment files, private keys, and other sensitive project assets.

We wanted a small security tool that could provide an immediate first layer of protection without requiring a large security stack.

Instead of trying to recreate every capability of a commercial security platform, we focused on three practical checks:

  • SecretScanner — detects likely hardcoded secrets and private-key material.
  • FileRiskScanner — detects sensitive files such as .env, SSH keys, credential files, and certificate containers.
  • DependencyScanner — detects dependency manifests and records declared dependencies without pretending that a dependency is automatically vulnerable.

The result is DevGuard: a modular scanner with both a CLI and a lightweight local dashboard.

The Architecture

The core of DevGuard is built around one simple idea: every scanner should return the same kind of result.

                 DevGuard
                     |
        +------------+------------+
        |            |            |
   SecretScanner  FileRiskScanner  DependencyScanner
        |            |            |
        +------------+------------+
                     |
              Finding Contract
                     |
                scan_project()
                 /          \
                /            \
              CLI          Web Dashboard
                             |
                   Score + Findings
Enter fullscreen mode Exit fullscreen mode

Each scanner implements the shared scanner interface and returns standardized Finding objects.

A finding contains:

  • file
  • line
  • rule
  • severity
  • message

For example:

Finding(
    file="path/to/file",
    line=12,
    rule="HARDCODED_SECRET",
    severity="HIGH",
    message="Possible hardcoded credential detected",
)
Enter fullscreen mode Exit fullscreen mode

This keeps the detection logic separate from reporting and presentation.

Adding another scanner therefore doesn't require redesigning the entire application.

From CLI Scanner to Local Dashboard

We didn't want DevGuard to stop at terminal output.

So we also built a lightweight local dashboard.

The backend uses Python's built-in http.server, while the frontend is plain HTML, CSS, and JavaScript.

There is no Flask.

There is no FastAPI.

There is no React dependency.

The local server exposes the scan API and serves the dashboard directly.

The dashboard presents:

  • overall security score
  • risk level
  • severity counts
  • scanner-wise findings
  • detailed finding information
  • recommended remediation

The goal is to turn a list of raw findings into something a developer can understand quickly.

Turning Findings Into an Explainable Security Score

Finding a security issue is useful.

Understanding its overall impact is even more useful.

DevGuard therefore includes a deterministic risk engine.

The score starts at 100 and applies a fixed penalty for every finding:

Severity Penalty
CRITICAL -30
HIGH -15
MEDIUM -7
LOW -2

The final score is constrained between 0 and 100.

The score maps to a risk level:

Score Risk
90–100 LOW
70–89 MEDIUM
40–69 HIGH
0–39 CRITICAL

We deliberately kept this calculation simple and explainable.

A developer should be able to understand why a score changed instead of trusting an unexplained security number.

Secret Detection Is More Than Searching for "password"

A naive scanner could search for words such as:

password
api_key
api_token
access_token
auth_token
secret_key
client_secret
private_key
Enter fullscreen mode Exit fullscreen mode

But keyword matching alone creates false positives.

For example:

password = os.getenv("PASSWORD")
Enter fullscreen mode Exit fullscreen mode

doesn't contain the actual password.

Compare that with:

password = "actual-secret-value"
Enter fullscreen mode Exit fullscreen mode

DevGuard checks for obvious environment lookups and placeholder values before reporting a likely hardcoded secret.

It also detects private-key material.

The lesson we learned was:

Finding more matches doesn't automatically make a security scanner better.

A useful scanner needs context.

Sometimes the Filename Is the Risk

Secrets don't always appear inside source code.

Sometimes the problem is the file itself.

DevGuard checks for sensitive assets such as:

.env
.env.*
id_rsa
id_dsa
id_ecdsa
id_ed25519
credentials.json
secrets.json
*.key
*.pem
*.p12
*.pfx
Enter fullscreen mode Exit fullscreen mode

But there is an important edge case.

A file named .env.example is commonly a safe template.

So DevGuard excludes known-safe environment templates such as:

.env.example
.env.sample
.env.template
Enter fullscreen mode Exit fullscreen mode

This is a small implementation detail, but it makes the scanner much more practical.

The Dependency Scanner Knows Its Limits

DevGuard recognizes several dependency manifest formats:

  • requirements.txt
  • package.json
  • go.mod
  • Cargo.toml
  • pom.xml
  • .csproj

It parses declared dependencies and reports the manifest as a finding.

But DevGuard deliberately does not claim that a dependency is vulnerable simply because it exists.

Finding a dependency and determining whether that dependency has a known vulnerability are two different problems.

We chose to keep that boundary explicit rather than produce misleading security claims.

What Turned Out Harder Than Expected

The hardest part wasn't importing re or `pathlib.

It was deciding what not to report.

We had to account for:

  • environment-variable lookups
  • placeholder values
  • binary files
  • test and fixture directories
  • generated directories
  • safe environment templates
  • different dependency manifest formats
  • consistent findings across scanners

For example, scanning a project blindly can lead to irrelevant results from directories such as:

text
.git/
.venv/
node_modules/
build/
dist/
tests/
fixtures/

DevGuard therefore maintains project-level exclusions.

The secret scanner also skips binary files.

These details aren't as visually impressive as a dashboard.

But they are the details that determine whether developers will actually trust and use a security scanner.

What the Standard Library Made Possible

The zero-dependency constraint forced us to look at Python's standard library differently.

We used standard-library modules for the core functionality:

Requirement Standard Library
HTTP server http.server
CLI parsing argparse
HTTP utilities urllib.request
JSON parsing json
TOML parsing tomllib
XML parsing xml.etree.ElementTree
File/path handling pathlib, os
Pattern matching re
Data models dataclasses
Analytics collections
Testing unittest

The point wasn't that third-party packages are bad.

Packages are valuable because they provide mature solutions and save development time.

The interesting question was:

How much functionality do we actually need, and how much of that functionality is already available?

We Documented the Substitutions Too

We created a STDLIB.md file to document the standard-library substitutions behind the project.

Some examples include:

  • requestsurllib.request
  • clickargparse
  • python-dotenvos
  • pathlib2pathlib
  • tomlitomllib
  • simplejsonjson

This wasn't about claiming that a standard-library module is always a complete replacement for a third-party package.

It was about documenting the smaller requirements DevGuard actually needed and why we chose not to add another runtime dependency.

Recommendations Instead of Just Findings

A scanner that only says "something is wrong" leaves the developer with the next problem:

What should I do about it?

DevGuard includes deterministic remediation recommendations for its known finding rules.

For example, a hardcoded secret can produce guidance to move the value to an environment variable or secure secret manager.

A detected private key can produce guidance to remove it and rotate or revoke it if it has been exposed.

A sensitive file can produce guidance around .gitignore and environment variables.

We wanted the output to be actionable rather than simply alarming.

Testing Without Adding a Test Framework

The zero-dependency principle also applies to testing.

DevGuard uses Python's built-in unittest.

The test suite covers areas including:

  • secret detection
  • private-key detection
  • environment-variable handling
  • binary-file handling
  • sensitive-file detection
  • dependency parsing
  • CLI integration
  • risk scoring
  • risk-level calculation
  • recommendation logic

The test command is:

bash
python -m unittest discover -s tests -v

Running DevGuard

From the project root, the CLI can be run with:

bash
python -m devguard scan .

If running directly from the source tree, the source directory can be placed on PYTHONPATH:

powershell
$env:PYTHONPATH="src"
python -m devguard scan .

The scanner returns a non-zero exit code when findings are detected, which makes the CLI suitable for automation.



What We Learned

The biggest lesson wasn't that third-party packages are unnecessary.

They aren't.

The lesson was that dependencies should solve a problem we actually have.

Without the usual libraries, we had to think more carefully about:

  • what the scanner really needed
  • how findings should be represented
  • how false positives should be reduced
  • how the dashboard should communicate risk
  • how much parsing was actually necessary
  • where a dependency would genuinely add value

The standard library didn't magically build DevGuard for us.

It gave us the building blocks.

We still had to design the architecture, handle edge cases, define the scanner contract, build the risk model, and decide what the tool should and should not claim.

The Empty requirements.txt Was the Easy Part

At first, "zero dependency" sounded like a restriction.

By the end, it felt more like an engineering exercise.

The interesting part wasn't avoiding pip commands.

It was learning to ask:

What problem are we actually solving?

Then:

What is the smallest reliable implementation we can build?

And finally:

Does the standard library already give us the pieces we need?

DevGuard is our answer to those questions.

A small security scanner.

A modular architecture.

A local dashboard.

An explainable risk model.

Actionable recommendations.

And a runtime that doesn't depend on a third-party security framework.

DevGuard at a Glance

Language: Python

Runtime dependencies: 0

Core focus: Developer security and codebase scanning

Scanners: Secret, File Risk, Dependency

Interfaces: CLI + local web dashboard

Testing: Python unittest

Standard-library documentation: STDLIB.md

Key Features

  • Hardcoded secret detection
  • Private-key detection
  • Sensitive-file detection
  • Dependency manifest analysis
  • Standardized Finding objects
  • Explainable security scoring
  • Risk-level classification
  • Remediation recommendations
  • Project and fixture exclusions
  • CLI scanning
  • Local dashboard
  • Zero third-party runtime dependencies

Try DevGuard

GitHub: https://github.com/Divyarani089/DevGuard.git

If you're building developer tooling under a zero-dependency constraint, we'd love to hear what you chose to implement yourself — and what you decided was still worth depending on.

Final Thought

DevGuard started with one constraint:

Build something useful without relying on third-party runtime packages.

That constraint changed how we thought about the entire project.

We didn't try to replace every security tool.

We focused on a specific problem, used the standard library where it made sense, documented the trade-offs, and built the smallest architecture that could support the features we wanted.

Don't start with the dependency. Start with the problem.

Top comments (0)