jsluice
is a Go package and command-line tool for extracting URLs, paths, secrets,
and other interesting data from JavaScript source code.
To install the command-line tool, run:
▶ go install github.com/BishopFox/jsluice/cmd/jsluice@latest
To add the package to your project, run:
▶ go get github.com/BishopFox/jsluice
Rather than using regular expressions alone, jsluice
uses go-tree-sitter
to look for places that URLs are known to be used,
such as being assigned to document.location
, passed to window.open()
, or passed to fetch()
etc.
A simple example program is provided here:
package main
import (
"encoding/json"
"fmt"
"github.com/BishopFox/jsluice"
)
func main() {
analyzer := jsluice.NewAnalyzer([]byte(`
const login = (redirect) => {
document.location = "/login?redirect=" + redirect + "&method=oauth"
}
`))
for _, url := range analyzer.GetURLs() {
j, err := json.MarshalIndent(url, "", " ")
if err != nil {
continue
}
fmt.Printf("%s\n", j)
}
}
Running the example:
▶ go run examples/basic/main.go
{
"url": "/login?redirect=EXPR\u0026method=oauth",
"queryParams": [
"method",
"redirect"
],
"bodyParams": [],
"method": "GET",
"type": "locationAssignment",
"source": "document.location = \"/login?redirect=\" + redirect + \"\u0026method=oauth\""
}
Note that the value of the redirect
query string parameter is EXPR
.
Code like this is common in JavaScript:
document.location = "/login?redirect=" + redirect + "&method=oauth"
jsluice
understands string concatenation, and replaces any expressions it cannot know the value
of with EXPR
. Although not a foolproof solution, this approach results in a valid URL or path
more often than not, and means that it's possible to discover things that aren't easily found using
other approaches. In this case, a naive regular expression may well miss the method
query string
parameter:
▶ JS='document.location = "/login?redirect=" + redirect + "&method=oauth"'
▶ echo $JS | grep -oE 'document\.location = "[^"]+"'
document.location = "/login?redirect="
jsluice
comes with some built-in URL matchers for common scenarios, but you can easily add more
with the AddURLMatcher
function:
package main
import (
"fmt"
"strings"
"github.com/BishopFox/jsluice"
)
func main() {
analyzer := jsluice.NewAnalyzer([]byte(`
var fn = () => {
var meta = {
contact: "mailto:[email protected]",
home: "https://example.com"
}
return meta
}
`))
analyzer.AddURLMatcher(
jsluice.URLMatcher{"string", func(n *jsluice.Node) *jsluice.URL {
val := n.DecodedString()
if !strings.HasPrefix(val, "mailto:") {
return nil
}
return &jsluice.URL{
URL: val,
Type: "mailto",
}
}},
)
for _, match := range analyzer.GetURLs() {
fmt.Println(match.URL)
}
}
There's a copy of this example here. You can run it like this:
▶ go run examples/urlmatcher/main.go
mailto:[email protected]
https://example.com
jsluice
doesn't match mailto:
URIs by default, it was found by the custom URLMatcher
.