Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/BishopFox/jsluice
Extract URLs, paths, secrets, and other interesting bits from JavaScript
https://github.com/BishopFox/jsluice
javascript security
Last synced: 3 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 (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-05-22T02:26:53.000Z (6 months ago)
- Last Synced: 2024-06-26T00:34:35.518Z (4 months ago)
- Topics: javascript, security
- Language: Go
- Homepage:
- Size: 2.32 MB
- Stars: 1,271
- Watchers: 14
- Forks: 82
- Open Issues: 5
-
Metadata Files:
- Readme: README.mkd
- License: LICENSE
Awesome Lists containing this project
- WebHackersWeapons - jsluice - analysis`](/categorize/tags/js-analysis.md)|![linux](/images/linux.png)![macos](/images/apple.png)![windows](/images/windows.png)[![Go](/images/go.png)](/categorize/langs/Go.md)| (Weapons / Tools)
README
# jsluice
[![Go Reference](https://pkg.go.dev/badge/github.com/BishopFox/jsluice.svg)](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:[email protected]",
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:[email protected]
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.