package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "log" "strconv" "time" "github.com/pkg/errors" ) var ( challenges = []Challenge{} seedFilePath *string user string ) func registerSeedFlags() { seedFilePath = flag.String("seedFile", "/etc/companion.json", "Path to seedfile") } // Read a given seed file and return their containing challenges func readSeedFile(path string) ([]Challenge, error) { var jsonContents map[string]interface{} // Read file rawContents, readError := ioutil.ReadFile(path) if readError != nil { log.Printf("Failed to read seed file at %s: %s", *seedFilePath, readError.Error()) return nil, readError } // Convert JSON String to map unmarshalError := json.Unmarshal(rawContents, &jsonContents) if unmarshalError != nil { log.Printf("Failed to parse JSON in seed file at %s: %s", *seedFilePath, unmarshalError.Error()) return nil, unmarshalError } tmpChallenges := []Challenge{} // Iterate over challenges for index, challengeObject := range jsonContents["challenges"].([]interface{}) { challenge := challengeObject.(map[string]interface{}) // add our parsed challenge to array name, nameOK := challenge["name"].(string) desc, descOK := challenge["description"].(string) flag, flagOK := challenge["flag"].(string) cont, contOK := challenge["container"].(string) category, categoryOK := challenge["category"].(string) pointsStr, pointsOK := challenge["points"].(string) points, _ := strconv.Atoi(pointsStr) if nameOK && descOK && flagOK && contOK && categoryOK && pointsOK { tmpChallenges = append(tmpChallenges, Challenge{ Name: name, Description: desc, Flag: flag, FoundFlag: time.Unix(0, 0), FlagTries: 0, Container: cont, Category: category, Points: points, }) } else { log.Printf("Ignoring challenge at position %d: Not all values are parseable (name: %b, desc: %b, flag: %b, container: %b, category: %b, points: %b", index, nameOK, descOK, flagOK, contOK, categoryOK, pointsOK) } } return tmpChallenges, nil } // Read the file we set up using flags and store the returned challenge array func getChallengesFromSeedFile() error { tmpChallenges, error := readSeedFile(*seedFilePath) if error != nil { // Couldn't read file, return error return error } if tmpChallenges == nil { // Could read file, but it didn't contain any parseable challenges - return return errors.New(fmt.Sprintf("Empty seed file at %s? No challenges found", *seedFilePath)) } if tmpChallenges != nil { // returned challenges found! Set them. challenges = tmpChallenges } return nil } // Generate a JSON string from the stored challenges, stripped from content which might spoil the user func generateJSONFromChallenges() (string, error) { // To avoid leakage of container name or the flag towards the user, we need to strip the challenges var strippedChallenges []StrippedChallenge categories := map[string]int{} for _, challenge := range challenges { // Append challenge to list strippedChallenges = append(strippedChallenges, stripChallenge(challenge)) categories[challenge.Category]++ } marshalled, marshalError := json.Marshal(map[string]interface{}{ "challenges": strippedChallenges, "categories": categories, }) return string(marshalled), marshalError } // Generate a JSON string from the stored challenges, just containing enough to call it statistics func generateJSONFromChallengesForStats() (string, error) { // To include only required information for statistics, we need to strip the challenge var strippedChallenges []StatsStrippedChallenge score := 0 categories := map[string]int{} for _, challenge := range challenges { // Append challenge to list strippedChallenges = append(strippedChallenges, stripChallengeForStatistics(challenge)) // Raise category counter categories[challenge.Category]++ // Raise score if flag was found if challenge.FoundFlag.Unix() > 0 { score = score + challenge.Points } } marshalled, marshalError := json.Marshal(map[string]interface{}{ "user": user, "challenges": strippedChallenges, "score": score, }) return string(marshalled), marshalError }