https://github.com/BishopFox/jsluice
Extract URLs, paths, secrets, and other interesting bits from JavaScript
https://github.com/BishopFox/jsluice
javascript security
Last synced: 9 months ago
JSON representation
Extract URLs, paths, secrets, and other interesting bits from JavaScript
- Host: GitHub
- URL: https://github.com/BishopFox/jsluice
- Owner: BishopFox
- License: mit
- Created: 2023-03-14T01:49:31.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2024-05-22T02:26:53.000Z (over 1 year ago)
- Last Synced: 2024-11-04T21:11:32.295Z (about 1 year ago)
- Topics: javascript, security
- Language: Go
- Homepage:
- Size: 2.32 MB
- Stars: 1,408
- Watchers: 14
- Forks: 92
- Open Issues: 7
-
Metadata Files:
- Readme: README.mkd
- License: LICENSE
Awesome Lists containing this project
- WebHackersWeapons - jsluice - analysis`](/categorize/tags/js-analysis.md)|[](/categorize/langs/Go.md)| (Weapons / Tools)
- osint_stuff_tool_collection - JSLUICE
- awesome-bugbounty-tools - jsluice - This tool extracts URLs, paths, secrets, and other interesting bits from JavaScript files. Values are extracted based not just on how they look, but also based on how they are used. (Recon / Links)
README
# jsluice
[](https://pkg.go.dev/github.com/BishopFox/jsluice)
`jsluice` is a Go package and [command-line tool](/cmd/jsluice/) for extracting URLs, paths, secrets,
and other interesting data from JavaScript source code.
If you want to do those things right away: look at the [command-line tool](/cmd/jsluice/).
If you want to integrate `jsluice`'s capabilities with your own project: look at the [examples](/examples/),
and read the [package documentation](https://pkg.go.dev/github.com/BishopFox/jsluice).
## Install
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
```
## Extracting URLs
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](/examples/basic/main.go):
```go
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:
```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="
```
### Custom URL Matchers
`jsluice` comes with some built-in URL matchers for common scenarios, but you can add more
with the `AddURLMatcher` function:
```go
analyzer := jsluice.NewAnalyzer([]byte(`
var fn = () => {
var meta = {
contact: "mailto:contact@example.com",
home: "https://example.com"
}
return meta
}
`))
analyzer.AddURLMatcher(
// The first value in the jsluice.URLMatcher struct is the type of node to look for.
// It can be one of "string", "assignment_expression", or "call_expression"
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](/examples/urlmatcher/main.go). You can run it like this:
```
▶ go run examples/urlmatcher/main.go
mailto:contact@example.com
https://example.com
```
`jsluice` doesn't match `mailto:` URIs by default, it was found by the custom `URLMatcher`.
## Extracting Secrets
As well as URLs, `jsluice` can extract secrets. As with URL extraction, custom matchers can
be supplied to supplement the default matchers. There's a short example program [here](/examples/secrets/main.go)
that does just that:
```go
analyzer := jsluice.NewAnalyzer([]byte(`
var config = {
apiKey: "AUTH_1a2b3c4d5e6f",
apiURL: "https://api.example.com/v2/"
}
`))
analyzer.AddSecretMatcher(
// The first value in the jsluice.SecretMatcher struct is a
// tree-sitter query to run on the JavaScript source.
jsluice.SecretMatcher{"(pair) @match", func(n *jsluice.Node) *jsluice.Secret {
key := n.ChildByFieldName("key").DecodedString()
value := n.ChildByFieldName("value").DecodedString()
if !strings.Contains(key, "api") {
return nil
}
if !strings.HasPrefix(value, "AUTH_") {
return nil
}
return &jsluice.Secret{
Kind: "fakeApi",
Data: map[string]string{
"key": key,
"value": value,
},
Severity: jsluice.SeverityLow,
Context: n.Parent().AsMap(),
}
}},
)
for _, match := range analyzer.GetSecrets() {
j, err := json.MarshalIndent(match, "", " ")
if err != nil {
continue
}
fmt.Printf("%s\n", j)
}
```
Running the example:
```
▶ go run examples/secrets/main.go
[2023-06-14T13:04:16+0100]
{
"kind": "fakeApi",
"data": {
"key": "apiKey",
"value": "AUTH_1a2b3c4d5e6f"
},
"severity": "low",
"context": {
"apiKey": "AUTH_1a2b3c4d5e6f",
"apiURL": "https://api.example.com/v2/"
}
}
```
Because we have a syntax tree available for the entire JavaScript source,
it was possible to inspect both the `key` and `value`, and also to easily
provide the parent object as context for the match.