# DSVW is a deliberately broken web app kept under 100 lines

> Damn Small Vulnerable Web is a Python application written in under 100 lines that is vulnerable on purpose, so that a security class has something to attack. It runs from a single file, it picks a port for you, and it has gained a stdlib only regression suite that walks every attack case and checks that it still works, plus a Docker image that runs it as a non root user.

**stamparm/DSVW** — Damn Small Vulnerable Web

- Repository: https://github.com/stamparm/DSVW
- Stars: 883 · Forks: 377
- Language: Python
- License: Unlicense
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/stamparm-dsvw

## Vulnerable on purpose, and small enough to read

Damn Small Vulnerable Web is a deliberately vulnerable web application written in under 100 lines of code, created for educational purposes. The README says it supports the majority of the most popular web application vulnerabilities together with appropriate attacks, and the title on the start-up banner calls it under 100 LoC, version 0.5, by Miroslav Stampar.

The scale is the point. A training target that is a thousand lines long would be a training target you have to read before you can use it, and a training target that is a framework would be one where the interesting behaviour is buried in configuration. Under a hundred lines means the whole application, its routes and its flaws, fit on one screen, so the attack is visible in the source rather than inferred from it.

The audience is a security class, a training workshop, or an individual who wants to practise the common classes of web application attack against something they are allowed to break. The licence is the Unlicense, which is the most permissive thing a repository can be, and the primary language is Python.

The README also shows the classic XSS screenshot, and the two names in the project, Damn Small Vulnerable Web and the DSVW abbreviation it runs under, are both in the banner. The project is from Stampar, whose name appears in the banner and in the repository, and it has been running for long enough that the version in the banner has reached 0.5.

One thing the repository now has that a teaching target of this kind usually does not is a test suite, and that is discussed further below.

## One command, a printed URL, and a port chosen for you

The quick start is a single command, and the output tells you where to go:

```bash
$ python3 dsvw.py 
Damn Small Vulnerable Web (DSVW) < 100 LoC (Lines of Code) #v0.5
 by: Miroslav Stampar (@stamparm)

[i] running HTTP server at 'http://127.0.0.1:65412'...
```

The server binds the loopback interface, so the application is reachable only from the machine it runs on, and the README then tells you to navigate your browser to the printed address.

The port and the address can both be changed, and the README gives the form explicitly: the --host=ADDR and --port=N options can be used to change the listening address and or port, with the example given being python3 dsvw.py --host=0.0.0.0 --port=8080.

That example deserves a second look, because it is the one line in the README with a security consequence. Binding to 0.0.0.0 makes an application that is intentionally full of injection flaws reachable from the network. For a classroom on a shared switch, or for a laptop on public wifi, that is how you accidentally publish a target. The default is the safe one, and the safe default is also the one the documentation demonstrates first, which is the right order.

The requirements are minimal. Python 3.x is required for running the program, and the repository root holds dsvw.py, requirements.txt, a Dockerfile, a docker-compose.yml, a LICENSE, the tests directory and the .github directory.

## Three of the attacks need lxml, and are disabled without it

Most of the vulnerability classes in DSVW work out of the box, but three of them have an optional dependency, and the README is explicit about the consequence.

The items XML External Entity local, XML External Entity remote and Blind XPath Injection, boolean, require installation of python-lxml, for example with apt-get install python-lxml. Otherwise, those will be disabled.

So the tool degrades in a defined way rather than failing to start. That is a sensible design for a training target, because the XXE and XPath cases need a real XML parser to demonstrate them, and lxml is not in the standard library. It also means the lesson plan has to notice: a workshop that assumes the XPath injection case is present on every student's laptop will be wrong on any machine without lxml.

The requirements file is one line, because there is one dependency:

```bash
lxml
```

and the README gives the pip route for installing it:

```bash
pip install -r requirements.txt
```

The Docker route installs it for you, since the image installs libxml2 and libxslt in the runtime stage. That is a small but practical detail, because a classroom that runs DSVW in containers gets the same set of enabled attacks on every machine.

## The Docker image builds a wheel and runs as an unprivileged user

The container side is short and does two things worth noting.

The build stage is python:3.10-alpine3.18 with the XML development headers and a compiler installed, and it uses pex to resolve the requirements into a single self-contained wrapper, with the pex version pinned in that pex~=2.1.47 form. The final stage is the same Alpine base, upgraded, with only the shared XML libraries installed, a non root user created with adduser, the application directory owned by that user, and the server started as that user.

The command at the end binds to every interface, which is correct in a container:

```dockerfile
EXPOSE 65412
USER dsvw
CMD ["/dsvw/pex_wrapper", "dsvw.py", "--host=0.0.0.0"]
```

That is the whole contrast with the default local run, and it is the right contrast to make. On a host, the application binds to 127.0.0.1 and only the person at the keyboard can reach it. In a container, it binds to 0.0.0.0 because the port has to be reachable from outside the container, and the isolation of the container is what makes that safe. A compose file ships alongside it, with a single service built from the local directory, the port exposed and published, and a restart policy of always.

```yaml
version: '3.9'

services:
  dsvw:
    build: .
    expose:
      - 65412
    ports:
      - 65412:65412
    restart: always
```

The README gives the equivalent one line command for people who would rather not use compose:

```bash
$ docker build . -t dsvw && docker run -p 65412:65412 dsvw
```

## A regression suite that checks every attack still works

The most interesting addition to this repository is the tests directory, and it changes what the project is.

The README says the tests directory holds a stdlib-only regression suite that starts dsvw.py itself, walks through every attack case listed on the index page and checks that each one still works. All targets are local, with a bundled HTTP fixture standing in for third-party sites, so the suite needs no Internet access.

That last property matters. A training target that depends on third party sites rots the moment one of them changes, and the fixture approach means the suite is hermetic. The run command is:

```bash
$ python3 -m unittest discover -s tests -t tests -v
```

The suite also has a graceful relationship with optional tooling. Tests requiring sqlmap, for the end-to-end exploitation of the SQL injection points, are skipped automatically when it is not installed, and the environment variable DSVW_SKIP_SLOW=1 leaves out the time-based one. So the suite runs to completion on a machine with nothing installed beyond Python, and it gets more thorough when you give it more tools.

The reason this is worth an engineer's attention is the nature of the assertion. Every other test suite in the world checks that safe behaviour still works. This one checks that the unsafe behaviour still works. For a teaching target that is exactly the right thing to assert, because the moment a vulnerability stops being exploitable, the exercise stops being useful, and nobody would notice from reading the code.

It also changes the maintenance model. A vulnerable application that silently stops being vulnerable is a training tool that has quietly stopped training anybody, and a suite that fails when that happens is a small piece of automation that keeps the teaching material honest.

## What DSVW is not, and the limits of the design

There are three boundaries worth stating, and one of them is a safety point rather than a technical one.

First, this is a target, not an application. Nothing about it is production quality, and nothing about it is meant to be. The under one hundred line constraint that makes it readable is the same constraint that rules out error handling, access control, and the defensive coding that any real service would need. If you are evaluating a web framework or a template, this is the wrong repository, and the README never claims otherwise.

Second, the coverage is the popular classes, not all of them. The README says the majority of the most popular web application vulnerabilities, and names the XXE and XPath cases explicitly. It is not a complete taxonomy of web security, and a curriculum built only from it will have gaps.

Third, and most important, the application is dangerous by design, and the documentation knows it. The example command that binds to 0.0.0.0 publishes an application with injection flaws to anyone who can reach the port. The safe default is the loopback interface, the Docker image is isolated by design, and the local HTTP fixture in the test suite exists so that nothing has to reach the internet. Those three details are the whole safety story, and they are all in place. The risk is a user who changes the host without changing where they run it.

On maintenance, the repository is not archived, the last push was on 2026-08-19, and there are no GitHub releases, so the version to look at is the one in the source tree, the banner's version 0.5. The Unlicense makes reuse unrestricted, and the presence of a test suite and a Docker image suggests someone is keeping the teaching material usable rather than merely publishing it.

## Conclusion

Adopt DSVW if you teach or learn web application security and you want one small target that exercises the common vulnerability classes without a lab to set up, or if you want a regression suite that proves each attack still works. Do not adopt it as an application to run on a shared network, because every weakness in it is intentional and the README shows how to bind it to every interface, and do not expect a maintained release stream, since the repository publishes no GitHub releases and the last push was on 2026-08-19. Before you use it, install lxml if you want the XXE and XPath cases enabled, run the test suite with python3 -m unittest discover -s tests -t tests -v, and check the port it printed before pointing a browser at it.

## FAQ

### What is DSVW and why is it deliberately vulnerable?

Damn Small Vulnerable Web is a Python web application written in under 100 lines of code, created for educational purposes. It supports the majority of the most popular web application vulnerabilities together with appropriate attacks, so that a class has something to practise against.

### How do I run the Damn Small Vulnerable Web application?

Run python3 dsvw.py, which starts an HTTP server and prints the address to open in your browser. The --host=ADDR and --port=N options change the listening address and port, and the default binds to 127.0.0.1 so only the local machine can reach it.

### Why are some DSVW attacks disabled?

The XML External Entity local and remote cases and the boolean Blind XPath Injection case require python-lxml, and without it those are disabled. Installing lxml with pip install -r requirements.txt enables them, and the Docker image includes the XML libraries already.

### How do the DSVW tests work?

The tests directory holds a stdlib-only regression suite that starts dsvw.py itself, walks every attack case listed on the index page and checks that each still works, using a bundled HTTP fixture so no Internet access is needed. Run it with python3 -m unittest discover -s tests -t tests -v.

### Can DSVW run in Docker?

Yes. The README gives docker build . -t dsvw && docker run -p 65412:65412 dsvw, and a docker-compose.yml with a single service. The image builds the application with pex and runs it as a non root user with the port bound to every interface inside the container.

## Sources

- [Issues](https://github.com/stamparm/DSVW/issues)
- [License: Unlicense](https://github.com/stamparm/DSVW/blob/master/LICENSE)
- [README](https://github.com/stamparm/DSVW/blob/master/README.md)
- [stamparm/DSVW on GitHub](https://github.com/stamparm/DSVW)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/stamparm-dsvw
