From 839c4452343954f396ff269eb52e9bb06fd0e7ad Mon Sep 17 00:00:00 2001 From: dmur1 <93072266+dmur1@users.noreply.github.com> Date: Wed, 12 Feb 2025 19:16:22 +0000 Subject: [PATCH] change the ctftime command to work with multiple urls at once fixes: https://github.com/rerrorctf/ret/issues/262 --- commands/ctftime.go | 64 ++++++++++++++++++++++++++------------------- commands/writeup.go | 9 +++++-- config/config.go | 38 ++++++++++++++------------- 3 files changed, 64 insertions(+), 47 deletions(-) diff --git a/commands/ctftime.go b/commands/ctftime.go index a618957..e68054b 100644 --- a/commands/ctftime.go +++ b/commands/ctftime.go @@ -29,37 +29,16 @@ func init() { } func CtfTimeHelp() string { - return "set the current ctftime url with ret\n\n" + - "the ctftime url is stored in " + theme.ColorCyan + "`~/.config/ret`" + theme.ColorReset + " using the " + theme.ColorYellow + "`\"ctftimeurl\"`" + theme.ColorReset + " field\n\n" + - "the command will use the ctftime.org api to fetch details about the currently set ctftime url and then display them\n\n" + + return "adds a ctftime url with ret\n\n" + + "the ctftime urls are stored in " + theme.ColorCyan + "`~/.config/ret`" + theme.ColorReset + " using the " + theme.ColorYellow + "`\"ctftimeurls\"`" + theme.ColorReset + " field\n\n" + + "the command will use the ctftime.org api to fetch details about all the currently set ctftime urls and then display them\n\n" + "the ctf's title, start time and finish time will be displayed along with an indication of the time to the start or finish depending on the context\n\n" + "for more details please see https://ctftime.org/api/\n\n" + - "the ctftime url will be used to aid in the generation of writeups with the " + theme.ColorGreen + "`writeup`" + theme.ColorReset + " command\n\n" + "the ctftime urls will be used to aid in the generation of writeups with the " + theme.ColorGreen + "`writeup`" + theme.ColorReset + " command\n\n" } -func CtfTime(args []string) { - if len(args) > 0 { - fmt.Printf(theme.ColorGray+"old ctftime url: "+theme.ColorRed+"%v"+theme.ColorReset+"\n", config.CtfTimeUrl) - - config.CtfTimeUrl = strings.Trim(args[0], "/") - - config.WriteUserConfig() - - fmt.Printf(theme.ColorGray+"new ctftime url: "+theme.ColorGreen+"%v"+theme.ColorReset+"\n", config.CtfTimeUrl) - return - } - - if len(config.CtfTimeUrl) == 0 { - return - } - - fmt.Printf(theme.ColorGray+"url: "+theme.ColorReset+"%v"+theme.ColorReset+"\n", config.CtfTimeUrl) - - if !strings.Contains(config.CtfTimeUrl, "ctftime.org") { - return - } - - splits := strings.Split(config.CtfTimeUrl, "/") +func showStats(ctfTimeUrl string) { + splits := strings.Split(ctfTimeUrl, "/") eventId := splits[len(splits)-1] url := fmt.Sprintf("https://ctftime.org/api/v1/events/%s/", eventId) @@ -121,3 +100,34 @@ func CtfTime(args []string) { fmt.Printf(theme.ColorGray+"time to finish: "+theme.ColorReset+"%v\n", finishTime.Sub(now)) } } + +func CtfTime(args []string) { + if len(args) > 0 { + newCtfTimeUrl := strings.Trim(args[0], "/") + + for _, ctfTimeUrl := range config.CtfTimeUrls { + if newCtfTimeUrl == ctfTimeUrl { + log.Fatalf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": a ctf with the url %v has already been registered\n", newCtfTimeUrl) + return + } + } + + config.CtfTimeUrls = append(config.CtfTimeUrls, newCtfTimeUrl) + + config.WriteUserConfig() + + fmt.Printf(theme.ColorGray+"new ctftime url: "+theme.ColorGreen+"%v"+theme.ColorReset+"\n", newCtfTimeUrl) + return + } + + for idx, ctfTimeUrl := range config.CtfTimeUrls { + fmt.Printf(theme.ColorGray+"url: "+theme.ColorReset+"%v"+theme.ColorReset+"\n", ctfTimeUrl) + + if strings.Contains(ctfTimeUrl, "ctftime.org") { + showStats(ctfTimeUrl) + if idx+1 < len(config.CtfTimeUrls) { + fmt.Printf("\n") + } + } + } +} diff --git a/commands/writeup.go b/commands/writeup.go index b6bea20..aafb4c7 100644 --- a/commands/writeup.go +++ b/commands/writeup.go @@ -27,7 +27,7 @@ func WriteupHelp() string { "the writeup will be saved in a file called `writeup.md`\n\n" + "if a file called `writeup.md` already exists the command will abort\n" + "there is a small window for a time-of-check/time-of-use race here - you have been warned!\n\n" + - "1. uses the " + theme.ColorYellow + "`\"ctftimeurl\"`" + theme.ColorReset + " to insert a url at the top of the writeup\n" + + "1. uses the first url from " + theme.ColorYellow + "`\"ctftimeurls\"`" + theme.ColorReset + " to insert a url at the top of the writeup\n" + "2. imports all notes taken with the " + theme.ColorGreen + "`notes`" + theme.ColorReset + " command into the description area\n" + "3. creates a space for a python script and then imports the script created by " + theme.ColorGreen + "`pwn`" + theme.ColorReset + " if it exists\n" + "4. imports the flag captured with the " + theme.ColorGreen + "`capture`" + theme.ColorReset + " command if it exists\n" + @@ -44,7 +44,12 @@ func Writeup(args []string) { log.Fatalf("💥 "+theme.ColorRed+"error"+theme.ColorReset+": \"%s\" already exists!\n", filePath) } - url := config.CtfTimeUrl + url := "" + if len(config.CtfTimeUrls) > 0 { + // we should do better than this + url = config.CtfTimeUrls[0] + } + if url == "" { url = "https://ctftime.link.goes.here" } diff --git a/config/config.go b/config/config.go index 75313ba..2352df5 100644 --- a/config/config.go +++ b/config/config.go @@ -37,24 +37,24 @@ var ( ChatWebhookUrl3 = "" GistToken = "" ChefUrl = "https://gchq.github.io/CyberChef/" - CtfTimeUrl = "" + CtfTimeUrls = []string{} ) type Config struct { - GhidraInstallPath string `json:"ghidrainstallpath"` - GhidraProject string `json:"ghidraproject"` - IdaInstallPath string `json:"idainstallpath"` - PwnScriptName string `json:"pwnscriptname"` - PwnScriptTemplate string `json:"pwnscripttemplate"` - CryptoScriptName string `json:"cryptoscriptname"` - CryptoScriptTemplate string `json:"cryptoscripttemplate"` - Username string `json:"username"` - ChatWebhookUrl string `json:"chatwebhookurl"` - ChatWebhookUrl2 string `json:"chatwebhookurl2"` - ChatWebhookUrl3 string `json:"chatwebhookurl3"` - GistToken string `json:"gisttoken"` - ChefUrl string `json:"chefurl"` - CtfTimeUrl string `json:"ctftimeurl"` + GhidraInstallPath string `json:"ghidrainstallpath"` + GhidraProject string `json:"ghidraproject"` + IdaInstallPath string `json:"idainstallpath"` + PwnScriptName string `json:"pwnscriptname"` + PwnScriptTemplate string `json:"pwnscripttemplate"` + CryptoScriptName string `json:"cryptoscriptname"` + CryptoScriptTemplate string `json:"cryptoscripttemplate"` + Username string `json:"username"` + ChatWebhookUrl string `json:"chatwebhookurl"` + ChatWebhookUrl2 string `json:"chatwebhookurl2"` + ChatWebhookUrl3 string `json:"chatwebhookurl3"` + GistToken string `json:"gisttoken"` + ChefUrl string `json:"chefurl"` + CtfTimeUrls []string `json:"ctftimeurls"` } func ParseUserConfig() { @@ -130,8 +130,9 @@ func ParseUserConfig() { ChefUrl = userConfig.ChefUrl } - if len(userConfig.CtfTimeUrl) > 0 { - CtfTimeUrl = userConfig.CtfTimeUrl + if len(userConfig.CtfTimeUrls) > 0 { + CtfTimeUrls = make([]string, len(userConfig.CtfTimeUrls)) + copy(CtfTimeUrls, userConfig.CtfTimeUrls) } } @@ -166,7 +167,8 @@ func WriteUserConfig() { userConfig.ChatWebhookUrl3 = ChatWebhookUrl3 userConfig.GistToken = GistToken userConfig.ChefUrl = ChefUrl - userConfig.CtfTimeUrl = CtfTimeUrl + userConfig.CtfTimeUrls = make([]string, len(CtfTimeUrls)) + copy(userConfig.CtfTimeUrls, CtfTimeUrls) jsonData, err := json.MarshalIndent(userConfig, "", " ") if err != nil {