Self-hosted service
003random/getJS avatar
003random/getJS

getJS pulls JavaScript source URLs out of HTML and HTTP responses

A tool to fastly get all javascript sources/files

903 stars122 forksGoMIT

At a glance

What is it?
getJS is a Go command line tool and library that lists the JavaScript files a page loads, with Docker images and a goquery based extractor. It suits anyone doing web review who needs a script inventory quickly, and it stops at what the HTML actually contains.
Who is it for?
getJS suits a pentester, bug bounty hunter or developer who needs a defensible list of the scripts a target serves, and who can live with a static view of the response. It is the wrong tool when the scripts only exist after client-side execution, because the extractor works on the HTML and never opens a browser.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 56 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A script inventory tool, not a crawler

getJS answers a narrow question that comes up during every web review: which JavaScript files does this page actually load? The README describes it as a tool for extracting JavaScript sources from URLs, web pages and HTTP responses, and it names its audience directly, pentesters, bug bounty hunters and developers who need the list fast. It ships in two forms, a command line interface for straightforward URL processing and a package interface for custom integrations.

The scope is deliberately narrow. There is no crawling, no recursion into discovered pages, no JavaScript execution and no rendering. What you get is a list of source URLs, optionally rewritten into absolute form and optionally resolved. That is a smaller promise than a full reconnaissance suite, and for its intended users it is the useful part: the answer to what does this page load, without spending minutes in a browser network panel. The tool is written in Go and depends on a single direct library, goquery, which is what parses the HTML. The result is a small static binary with no runtime to install.

Fetch, extract, then filter are three separate steps

The package example in the README shows the internal shape of the tool, and it is the clearest description of the mechanism. A response is fetched with extractor.FetchResponse, which takes a URL string, a request method and an http.Header. The body is then handed to extractor.ExtractSources together with a map of extraction points, and the resulting list goes through extractor.Filter, where options such as WithComplete and WithResolve are applied.

The extraction points map is where the behaviour is genuinely configurable. The example passes entries for the script element with src and data-src, and for the anchor element with href, which shows that the default is not a fixed list of attributes you cannot change. The filter stage is separate from extraction, which means you can pull every link out of a page and get exactly the same pipeline as the script case.

Because the extractor works on the HTML that the server returned, anything assembled later by client-side code is invisible to it. A single page application that builds its bundle at runtime will return a page whose HTML says very little, and getJS will faithfully report that. The flag named -resolve changes what happens next to a discovered script file rather than what the HTML contains, so it does not close that gap either.

Installing the binary and running the first extraction

Installation is a single go install of the v2 module path, and the v2 suffix matters because the module was renamed for the major version:

bash
go install github.com/003random/getJS/v2@latest

With the binary on your path, the shortest useful run points it at a URL:

bash
getJS -url https://destroy.ai

The tool also reads a page from standard input, which is the route to use when you already have a response captured by something else:

bash
curl https://destroy.ai | getJS

Results are printed to standard output, so redirecting them to a file is the normal way to keep a record of what a target served:

bash
getJS -url "http://example.com" -output results.txt

The -output flag is optional and, like -input, can be given more than once. The -input flag takes a plain text file with one URL per line, which is how a list of hosts gets processed in a single pass. The version you install is the v2 line, and the older original is kept under a v1 tag, so a script that assumes the pre-v2 behaviour needs checking before it is run against a v2 binary.

The flags that decide what actually comes back

The option list is short enough to read in full, which is unusual and useful. -url names the target. -input and -output handle files and can be repeated. -method sets the request method and defaults to GET, and -header adds request headers and can also be repeated, so a target that answers differently based on User-Agent can be reached without touching the source. -timeout sets the per-request timeout and defaults to 5s, and -threads controls how many processing threads are spawned, defaulting to 2. -verbose prints runtime information and errors.

The two that change the output are -complete and -resolve, and they are coupled. -complete completes or autofills relative URLs by adding the current origin, turning /static/app.js into an absolute URL. -resolve resolves the JavaScript files themselves, and the README states it can only be used in combination with --complete. That dependency is not cosmetic: without a base URL there is nothing to resolve a relative path against.

A run with a custom method, a header and a longer timeout looks like this:

bash
getJS -url "http://example.com" -header "User-Agent: foo bar" -method POST --timeout=15s

Note the mixing of single and double dash forms. Both appear in the documentation, and the flag parser appears to accept either, but a script should not assume a preference.

A scratch Docker image with no shell to pipe into

The container story is more carefully built than most, and the Dockerfile explains why the instructions are written the way they are. The build stage uses a pinned Go image, copies the module files and runs go mod download followed by go mod verify before the source is copied, and produces a static binary with CGO disabled, trimpath and stripped symbols. The runtime stage is FROM scratch, so there is no shell, no package manager and no operating system in the final image.

Building and running it takes two commands:

bash
docker build -t getjs:local .
bash
docker run --rm getjs:local -url https://example.com

Piping into the container needs --interactive, because docker run does not keep stdin open without it, so a piped URL or HTTP response has nowhere to go:

bash
curl https://example.com | docker run --rm --interactive getjs:local

List files are mounted read-only and referenced by their container path, which keeps the container from needing write access at all:

bash
docker run --rm --volume "${PWD}:/data:ro" getjs:local -input /data/urls.txt

Results still go to standard output, so saving them happens on the host:

bash
docker run --rm getjs:local -url https://example.com > results.txt

The final image runs as the non-root user 65532 and its entrypoint is the binary itself, and the CA certificate bundle is copied in by hand because a scratch image has none of its own.

Using the extractor as a Go package

The library route exists for people who want the list inside their own program, and it is the more capable half of the project. The import path is the extractor package of the same module:

go
    resp, err := extractor.FetchResponse(baseURL.String(), "GET", http.Header{})
    if (err != nil) {
        log.Fatalf("Error fetching response: %v", err)
    }
    defer resp.Body.Close()

    // Custom extraction points (optional).
    extractionPoints := map[string][]string{
        "script": {"src", "data-src"},
        "a": {"href"},
    }

    sources, err := extractor.ExtractSources(resp.Body, extractionPoints)

The filter stage then applies the same options the command line exposes, and the result is a set you can iterate:

go
    filtered, err := extractor.Filter(sources, extractor.WithComplete(baseURL), extractor.WithResolve())
    if (err != nil) {
        log.Fatalf("Error filtering sources: %v", err)
    }

    for source := range filtered {
        fmt.Println(source.String())
    }

The useful detail is that the extraction points are a parameter rather than a constant. A scanner that wants to inventory stylesheets, module preloads or source maps can express that in the map and reuse the fetch and filter stages, and the same Filter options keep the output shape consistent with the command line tool.

Where the static view runs out, and how the project is versioned

The limitation worth stating is the one from the first section, because it decides when the tool is the wrong choice. The extractor parses the HTML response, so a target that assembles its JavaScript after the page loads produces a short or empty result. There is no browser in the toolchain, no script execution and no wait for network idle, and no flag in the option list adds any of that. For a review of a server-rendered site this is exactly right. For a single page application it is a partial answer.

There is a second, smaller limit. The direct dependency set is one library, goquery, which is why the binary is small and the build is fast, but it also means the parsing is HTML only. Markup that arrives as a JSON payload, or a script tag written by the server inside a comment, is outside what the parser sees.

On maintenance and licensing, the picture is plain. The licence is MIT, with the licence file at the top level of the repository, and the README points to it for the terms. The most recent release is v2.0.0 from 2024-07-07, while the last push to the repository was on 2026-08-29, so work has continued on the branch without a new tagged release. The module declares Go 1.22, and the Dockerfile builds with a newer Go 1.27.0 image, so the toolchain in the container is ahead of the language version the module file claims. The default branch is master, not main, which matters for anyone scripting a raw clone.

Editorial conclusion

getJS suits a pentester, bug bounty hunter or developer who needs a defensible list of the scripts a target serves, and who can live with a static view of the response. It is the wrong tool when the scripts only exist after client-side execution, because the extractor works on the HTML and never opens a browser. Before you rely on it, check that the target's JavaScript is present in the served HTML rather than assembled at runtime, confirm that the -resolve flag you want is paired with -complete, and read the MIT licence file in the repository root, which covers the code but not the targets you point it at.

Frequently asked questions

How do I install getJS?

The README gives one command: go install github.com/003random/getJS/v2@latest. The v2 suffix in the module path is required, and the pre-v2 version of the tool is kept under a v1 tag.

Can getJS read a page from standard input?

Yes. The README shows a response being piped in with curl https://destroy.ai | getJS. When you do this in Docker you have to add --interactive, because docker run does not keep standard input open without it.

Does the -resolve flag work on its own?

No. The option list states that -resolve can only be used in combination with --complete, which completes relative URLs by adding the current origin and therefore supplies the base that resolution needs.

Which licence is getJS published under?

MIT, with the licence file in the repository root and the README linking to it for the full terms. The MIT licence covers the code, not the sites you point the tool at.

How do I get the JavaScript files a single page application loads?

getJS works on the HTML the server returns, so scripts assembled at runtime are not visible to it. The tool has no browser and no script execution, which means a client-rendered application needs a different approach to enumerate its bundles.

Official sources

  1. 003random/getJS on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/003random-getjs.svg)](https://hysenlabs.com/projects/003random-getjs)

Was this useful?