{"id":15035243,"url":"https://github.com/bishopfox/jsluice","last_synced_at":"2025-04-13T18:37:51.364Z","repository":{"id":176386679,"uuid":"613650366","full_name":"BishopFox/jsluice","owner":"BishopFox","description":"Extract URLs, paths, secrets, and other interesting bits from JavaScript","archived":false,"fork":false,"pushed_at":"2024-05-22T02:26:53.000Z","size":2431,"stargazers_count":1522,"open_issues_count":8,"forks_count":107,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-04-06T15:09:36.072Z","etag":null,"topics":["javascript","security"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BishopFox.png","metadata":{"files":{"readme":"README.mkd","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-14T01:49:31.000Z","updated_at":"2025-04-05T15:27:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"573c4e82-0011-4953-9aa8-b005b86b71f5","html_url":"https://github.com/BishopFox/jsluice","commit_stats":null,"previous_names":["bishopfox/jsluice"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BishopFox%2Fjsluice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BishopFox%2Fjsluice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BishopFox%2Fjsluice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BishopFox%2Fjsluice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BishopFox","download_url":"https://codeload.github.com/BishopFox/jsluice/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248762456,"owners_count":21157738,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","security"],"created_at":"2024-09-24T20:27:56.749Z","updated_at":"2025-04-13T18:37:51.340Z","avatar_url":"https://github.com/BishopFox.png","language":"Go","readme":"# jsluice\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/BishopFox/jsluice.svg)](https://pkg.go.dev/github.com/BishopFox/jsluice)\n\n`jsluice` is a Go package and [command-line tool](/cmd/jsluice/) for extracting URLs, paths, secrets,\nand other interesting data from JavaScript source code.\n\nIf you want to do those things right away: look at the [command-line tool](/cmd/jsluice/).\n\nIf you want to integrate `jsluice`'s capabilities with your own project: look at the [examples](/examples/),\nand read the [package documentation](https://pkg.go.dev/github.com/BishopFox/jsluice).\n\n## Install\n\nTo install the command-line tool, run:\n\n```\n▶ go install github.com/BishopFox/jsluice/cmd/jsluice@latest\n```\n\nTo add the package to your project, run:\n\n```\n▶ go get github.com/BishopFox/jsluice\n```\n\n## Extracting URLs\n\nRather than using regular expressions alone, `jsluice` uses `go-tree-sitter` to look for places that URLs are known to be used,\nsuch as being assigned to `document.location`, passed to `window.open()`, or passed to `fetch()` etc.\n\nA simple example program is provided [here](/examples/basic/main.go):\n\n```go\nanalyzer := jsluice.NewAnalyzer([]byte(`\n    const login = (redirect) =\u003e {\n        document.location = \"/login?redirect=\" + redirect + \"\u0026method=oauth\"\n    }\n`))\n\nfor _, url := range analyzer.GetURLs() {\n    j, err := json.MarshalIndent(url, \"\", \"  \")\n    if err != nil {\n        continue\n    }\n\n    fmt.Printf(\"%s\\n\", j)\n}\n```\n\nRunning the example:\n```\n▶ go run examples/basic/main.go\n{\n  \"url\": \"/login?redirect=EXPR\\u0026method=oauth\",\n  \"queryParams\": [\n    \"method\",\n    \"redirect\"\n  ],\n  \"bodyParams\": [],\n  \"method\": \"GET\",\n  \"type\": \"locationAssignment\",\n  \"source\": \"document.location = \\\"/login?redirect=\\\" + redirect + \\\"\\u0026method=oauth\\\"\"\n}\n```\n\nNote that the value of the `redirect` query string parameter is `EXPR`.\nCode like this is common in JavaScript:\n\n```javascript\ndocument.location = \"/login?redirect=\" + redirect + \"\u0026method=oauth\"\n```\n\n`jsluice` understands string concatenation, and replaces any expressions it cannot know the value\nof with `EXPR`. Although not a foolproof solution, this approach results in a valid URL or path\nmore often than not, and means that it's possible to discover things that aren't easily found using\nother approaches. In this case, a naive regular expression may well miss the `method` query string\nparameter:\n\n```\n▶ JS='document.location = \"/login?redirect=\" + redirect + \"\u0026method=oauth\"'\n▶ echo $JS | grep -oE 'document\\.location = \"[^\"]+\"'\ndocument.location = \"/login?redirect=\"\n```\n\n### Custom URL Matchers\n\n`jsluice` comes with some built-in URL matchers for common scenarios, but you can add more\nwith the `AddURLMatcher` function:\n\n```go\nanalyzer := jsluice.NewAnalyzer([]byte(`\n    var fn = () =\u003e {\n        var meta = {\n            contact: \"mailto:contact@example.com\",\n            home: \"https://example.com\"\n        }\n        return meta\n    }\n`))\n\nanalyzer.AddURLMatcher(\n    // The first value in the jsluice.URLMatcher struct is the type of node to look for.\n    // It can be one of \"string\", \"assignment_expression\", or \"call_expression\"\n    jsluice.URLMatcher{\"string\", func(n *jsluice.Node) *jsluice.URL {\n        val := n.DecodedString()\n        if !strings.HasPrefix(val, \"mailto:\") {\n            return nil\n        }\n\n        return \u0026jsluice.URL{\n            URL:  val,\n            Type: \"mailto\",\n        }\n    }},\n)\n\nfor _, match := range analyzer.GetURLs() {\n    fmt.Println(match.URL)\n}\n```\n\nThere's a copy of this example [here](/examples/urlmatcher/main.go). You can run it like this:\n\n```\n▶ go run examples/urlmatcher/main.go\nmailto:contact@example.com\nhttps://example.com\n```\n\n`jsluice` doesn't match `mailto:` URIs by default, it was found by the custom `URLMatcher`.\n\n\n## Extracting Secrets\n\nAs well as URLs, `jsluice` can extract secrets. As with URL extraction, custom matchers can\nbe supplied to supplement the default matchers. There's a short example program [here](/examples/secrets/main.go)\nthat does just that:\n\n```go\nanalyzer := jsluice.NewAnalyzer([]byte(`\n    var config = {\n        apiKey: \"AUTH_1a2b3c4d5e6f\",\n        apiURL: \"https://api.example.com/v2/\"\n    }\n`))\n\nanalyzer.AddSecretMatcher(\n    // The first value in the jsluice.SecretMatcher struct is a\n    // tree-sitter query to run on the JavaScript source.\n    jsluice.SecretMatcher{\"(pair) @match\", func(n *jsluice.Node) *jsluice.Secret {\n        key := n.ChildByFieldName(\"key\").DecodedString()\n        value := n.ChildByFieldName(\"value\").DecodedString()\n\n        if !strings.Contains(key, \"api\") {\n            return nil\n        }\n\n        if !strings.HasPrefix(value, \"AUTH_\") {\n            return nil\n        }\n\n        return \u0026jsluice.Secret{\n            Kind: \"fakeApi\",\n            Data: map[string]string{\n                \"key\":   key,\n                \"value\": value,\n            },\n            Severity: jsluice.SeverityLow,\n            Context:  n.Parent().AsMap(),\n        }\n    }},\n)\n\nfor _, match := range analyzer.GetSecrets() {\n    j, err := json.MarshalIndent(match, \"\", \"  \")\n    if err != nil {\n        continue\n    }\n\n    fmt.Printf(\"%s\\n\", j)\n}\n```\n\nRunning the example:\n\n```\n▶ go run examples/secrets/main.go\n[2023-06-14T13:04:16+0100]\n{\n  \"kind\": \"fakeApi\",\n  \"data\": {\n    \"key\": \"apiKey\",\n    \"value\": \"AUTH_1a2b3c4d5e6f\"\n  },\n  \"severity\": \"low\",\n  \"context\": {\n    \"apiKey\": \"AUTH_1a2b3c4d5e6f\",\n    \"apiURL\": \"https://api.example.com/v2/\"\n  }\n}\n```\n\nBecause we have a syntax tree available for the entire JavaScript source,\nit was possible to inspect both the `key` and `value`, and also to easily\nprovide the parent object as context for the match.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbishopfox%2Fjsluice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbishopfox%2Fjsluice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbishopfox%2Fjsluice/lists"}