Skip to content

Commit 4566b51

Browse files
Merge pull request #43 from ItsIgnacioPortal/patch
v6.2.1
2 parents 0ca77dc + b7137ab commit 4566b51

2 files changed

Lines changed: 88 additions & 45 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ Usage: hacker-scoper --file /path/to/targets [--company company | --inscopes-fil
106106
| | --enable-private-tlds | Set this flag to enable the use of company scope domains with private TLDs. This essentially disables the bug-bounty-program misconfiguration detection. |
107107
| -ch | --chain-mode<br>--raw<br>--plain | In "chain-mode" we only output the important information. No decorations. |
108108
| | --database /path/to/database | Custom path to the cached firebounty database |
109+
| | --update-db | Set this flag to update the database and then exit immediately. |
110+
| | --no-update | Set this flag to avoid checking for updates during this run. |
109111
| -iu | --include-unsure | Include "unsure" assets in the output. An unsure asset is an asset that's not in scope, but is also not out of scope. Very probably unrelated to the bug bounty program. |
110112
| -o | --output /path/to/outputfile | Save the inscope assets to a file |
111113
| | --csv | Output in CSV format |

src/hacker-scoper/main.go

Lines changed: 86 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ func main() {
113113

114114
var quietMode bool
115115
var showVersion bool
116+
var updateAndQuit bool
117+
var skipUpdateCheck bool
116118
var company string
117119
var inscopeExplicitLevel int //should only be [0], 1, or 2
118120
var noscopeExplicitLevel int //should only be [0], 1, or 2
@@ -168,14 +170,19 @@ func main() {
168170
169171
-ch, --chain-mode, --plain, --raw, --no-ansi
170172
In "chain-mode" we only output the important information. No decorations.
171-
Default: false
172173
173174
--database /path/to/database
174175
Custom path to the cached firebounty database.
175176
Default:
176177
- Windows: %APPDATA%\hacker-scoper\
177178
- Linux: /etc/hacker-scoper/
178179
180+
--update-db
181+
Set this flag to update the database and then exit immediately.
182+
183+
--no-update
184+
Set this flag to avoid checking for updates during this run.
185+
179186
-iu, --include-unsure
180187
Include "unsure" assets in the output. An unsure asset is an asset that's not in scope, but is also not out of scope. Very probably unrelated to the bug bounty program.
181188
@@ -223,6 +230,11 @@ func main() {
223230
flag.BoolVar(&chainMode, "raw", false, "Output only the important information. No decorations.")
224231
flag.BoolVar(&chainMode, "no-ansi", false, "Output only the important information. No decorations.")
225232
flag.StringVar(&firebountyJSONPath, "database", "", "Custom path to the cached firebounty database")
233+
flag.BoolVar(&updateAndQuit, "update-db", false, "Set this flag to update the database and then exit immediately.")
234+
flag.BoolVar(&updateAndQuit, "db-update", false, "Set this flag to update the database and then exit immediately.")
235+
flag.BoolVar(&updateAndQuit, "update", false, "Set this flag to update the database and then exit immediately.")
236+
flag.BoolVar(&skipUpdateCheck, "no-update", false, "Set this flag to avoid checking for updates during this run.")
237+
flag.BoolVar(&skipUpdateCheck, "skip-update", false, "Set this flag to avoid checking for updates during this run.")
226238
flag.StringVar(&inscopeOutputFile, "o", "", "Save the inscope urls to a file")
227239
flag.StringVar(&inscopeOutputFile, "output", "", "Save the inscope urls to a file")
228240
flag.BoolVar(&outputCSVFormat, "csv", false, "Output in CSV format")
@@ -247,7 +259,7 @@ func main() {
247259
`
248260

249261
if showVersion {
250-
fmt.Print("hacker-scoper: v6.2.0\n")
262+
fmt.Print("hacker-scoper: v6.2.1\n")
251263
os.Exit(0)
252264
}
253265

@@ -284,28 +296,31 @@ func main() {
284296
if firebountyJSONPath == "" && !chainMode {
285297
warning("This OS isn't officially supported. The firebounty JSON will be downloaded in the current working directory. To override this behavior, use the \"--database\" flag.")
286298
}
287-
} else {
288-
//If the folder exists...
289-
_, err := os.Stat(firebountyJSONPath)
290-
if errors.Is(err, os.ErrNotExist) {
291-
//Create the folder
292-
err := os.Mkdir(firebountyJSONPath, 0700)
293-
if err != nil {
294-
crash("Unable to create the folder \""+firebountyJSONPath+"\"", err)
295-
}
296-
} else if err != nil {
297-
// Schrodinger: file may or may not exist. See err for details.
298-
crash("Could not verify existence of the folder \""+firebountyJSONPath+"\"!", err)
299-
}
299+
firebountyJSONPath = firebountyJSONPath + firebountyJSONFilename
300300
}
301301

302-
firebountyJSONPath = firebountyJSONPath + firebountyJSONFilename
303-
304302
if !chainMode {
305303
fmt.Println(banner)
306304
}
307305

308-
//validate arguments
306+
if updateAndQuit && chainMode {
307+
warning("--update-db is not compatible with chain-mode. Use the program exit-code to verify update success programmatically.")
308+
os.Exit(2)
309+
}
310+
311+
if updateAndQuit {
312+
fmt.Println("[INFO]: Checking for database updates...")
313+
err := checkForDatabaseUpdates(&databaseIsUpdating, tmpFile)
314+
315+
if err != nil {
316+
os.Exit(1)
317+
} else {
318+
fmt.Println("[INFO]: The database is up-to-date. Exiting...")
319+
os.Exit(0)
320+
}
321+
}
322+
323+
//validate scope levels
309324
if inscopeExplicitLevel != 1 && inscopeExplicitLevel != 2 && inscopeExplicitLevel != 3 {
310325
var err error
311326
crash("Invalid in-scope explicit-level selected", err)
@@ -315,6 +330,14 @@ func main() {
315330
crash("Invalid no-scope explicit-level selected", err)
316331
}
317332

333+
//validate filepath params
334+
if scopesListFilepath == "" && outofScopesListFilepath != "" {
335+
if !chainMode {
336+
warning("out-of-scope filepath specified, but no in-scope filepath specified. Either both or neither must be specified.")
337+
}
338+
os.Exit(2)
339+
}
340+
318341
// Validate the targets input
319342
var streamedLinesChan <-chan string
320343

@@ -408,25 +431,8 @@ func main() {
408431
} else if company != "" {
409432
// If the user inputted a company name, we'll lookup said company in the firebounty db
410433

411-
// If the db exists...
412-
if firebountyJSONFileStats, err := os.Stat(firebountyJSONPath); err == nil {
413-
//check age. if age > 24hs
414-
yesterday := time.Now().Add(-24 * time.Hour)
415-
if firebountyJSONFileStats.ModTime().Before(yesterday) {
416-
if !chainMode {
417-
fmt.Println("[INFO]: +24hs have passed since the last update to the local firebounty database. Updating...")
418-
}
419-
updateFireBountyJSON(&databaseIsUpdating, tmpFile, true)
420-
}
421-
} else if errors.Is(err, os.ErrNotExist) {
422-
// The database does not exist.
423-
// We'll create it.
424-
if !chainMode {
425-
fmt.Println("[INFO]: Downloading scopes file and saving in \"" + firebountyJSONPath + "\"")
426-
}
427-
updateFireBountyJSON(&databaseIsUpdating, tmpFile, false)
428-
} else {
429-
crash("Unable to get information about the database file at \""+firebountyJSONPath+"\". Probably a permissions error with the directory the database is saved at. Try using the database argument like '--database /custom/path/to/store/the/firebounty.json'", err)
434+
if !skipUpdateCheck {
435+
_ = checkForDatabaseUpdates(&databaseIsUpdating, tmpFile) // #nosec G104 -- A failed database update isn't dangerous.
430436
}
431437

432438
// Get the company names from the JSON file
@@ -664,10 +670,10 @@ func main() {
664670
if outputCSVFormat {
665671
if res.isUnsure {
666672
if includeUnsure {
667-
fmt.Println("unsure," + target)
673+
fmt.Println("unsure,\"" + target + "\"")
668674
}
669675
} else {
670-
fmt.Println("inscope," + target)
676+
fmt.Println("inscope,\"" + target + "\"")
671677
}
672678
} else {
673679
if res.isUnsure {
@@ -692,13 +698,13 @@ func main() {
692698
if outputCSVFormat {
693699
if res.isUnsure {
694700
if includeUnsure {
695-
_, err = writer.WriteString("unsure," + target + "\n")
701+
_, err = writer.WriteString("unsure,\"" + target + "\"\n")
696702
if err != nil {
697703
crash("Unable to write to output file", err)
698704
}
699705
}
700706
} else {
701-
_, err = writer.WriteString("inscope," + target + "\n")
707+
_, err = writer.WriteString("inscope,\"" + target + "\"\n")
702708
if err != nil {
703709
crash("Unable to write to output file", err)
704710
}
@@ -726,16 +732,22 @@ func main() {
726732

727733
}
728734

729-
func updateFireBountyJSON(databaseIsUpdating *bool, tmpFile *os.File, dbFileExists bool) {
735+
func updateFireBountyJSON(databaseIsUpdating *bool, tmpFile *os.File) error {
730736
*databaseIsUpdating = true
731737
//get the big JSON from the API
732738
req, err := http.NewRequest("GET", firebountyAPIURL, nil)
733739
if err != nil {
734740
crash("Could not download scopes from firebounty at: "+firebountyAPIURL, err)
735741
}
736-
jason, _ := http.DefaultClient.Do(req)
742+
jason, err := http.DefaultClient.Do(req)
743+
if err != nil {
744+
if !chainMode {
745+
warning("There was an error contacting the firebounty server.")
746+
}
747+
*databaseIsUpdating = false
748+
return err
749+
}
737750

738-
//f, _ := os.OpenFile(firebountyJSONPath, os.O_CREATE|os.O_WRONLY, 0600)
739751
tmpFile, err = os.CreateTemp("", "hacker-scoper_tmp-db")
740752
if err != nil {
741753
crash("Error creating temporary file.", err)
@@ -748,7 +760,8 @@ func updateFireBountyJSON(databaseIsUpdating *bool, tmpFile *os.File, dbFileExis
748760
_, err = io.Copy(io.MultiWriter(tmpFile, bar), jason.Body)
749761
if err != nil {
750762
warning("Error writing to the temporary file at \"" + tmpFile.Name() + "\". Database update cancelled.")
751-
return
763+
*databaseIsUpdating = false
764+
return nil
752765
}
753766
jason.Body.Close() // #nosec G104 -- There is no situation in which closing the body of the request will cause an error.
754767
tmpFile.Close() // #nosec G104 -- There is no situation in which closing the temp file will cause an error.
@@ -766,6 +779,34 @@ func updateFireBountyJSON(databaseIsUpdating *bool, tmpFile *os.File, dbFileExis
766779
warning("Error deleting temp file at \"" + tmpFile.Name() + "\". Please ensure the file is deleted.")
767780
}
768781
}
782+
*databaseIsUpdating = false
783+
return nil
784+
}
785+
786+
func checkForDatabaseUpdates(databaseIsUpdating *bool, tmpFile *os.File) error {
787+
// If the db exists...
788+
if firebountyJSONFileStats, err := os.Stat(firebountyJSONPath); err == nil {
789+
//check age. if age > 24hs
790+
yesterday := time.Now().Add(-24 * time.Hour)
791+
if firebountyJSONFileStats.ModTime().Before(yesterday) {
792+
if !chainMode {
793+
fmt.Println("[INFO]: +24hs have passed since the last update to the local firebounty database. Starting update, please wait...")
794+
}
795+
return updateFireBountyJSON(databaseIsUpdating, tmpFile)
796+
}
797+
} else if errors.Is(err, os.ErrNotExist) {
798+
// The database does not exist.
799+
// We'll create it.
800+
if !chainMode {
801+
fmt.Println("[INFO]: Downloading scopes file and saving in \"" + firebountyJSONPath + "\"")
802+
}
803+
return updateFireBountyJSON(databaseIsUpdating, tmpFile)
804+
} else {
805+
crash("Unable to get information about the database file at \""+firebountyJSONPath+"\". Probably a permissions error with the directory the database is saved at. Try using the database argument like '--database /custom/path/to/store/the/firebounty.json'", err)
806+
}
807+
808+
// This last return is unreachable but it must be added to appease the compiler.
809+
return nil
769810
}
770811

771812
func parseScopes(inscopeScopes *[]interface{}, noscopeScopes *[]interface{}, target *interface{}, inscopeExplicitLevel *int, noscopeExplicitLevel *int, includeUnsure bool) (isInsideScope bool, isUnsure bool) {

0 commit comments

Comments
 (0)