Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/challenge.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type Challenge struct {
Location string `json:"location"`
Identifier any `json:"identifier"`
} `json:"dockerfile_locations,omitempty"`
HandoutDir string `json:"handout_dir,omitempty"`
Zip *bool `json:"zip,omitempty"`
}

type ChallengeConfig struct {
Expand Down Expand Up @@ -60,7 +62,10 @@ func jsonFormatChallengeConfig(challengeConfig *ChallengeConfig) string {
}

func filesDir(challengeConfig *ChallengeConfig) string {
return "k8s/files"
if challengeConfig.Challenge.HandoutDir != "" {
return challengeConfig.Challenge.HandoutDir
}
return "handout"
}
Comment on lines +64 to 68

func filesDirPath(challengeConfig *ChallengeConfig) string {
Expand All @@ -69,6 +74,17 @@ func filesDirPath(challengeConfig *ChallengeConfig) string {
return challengeConfig.Path + "/" + filesDir
}

func shouldZip(challengeConfig *ChallengeConfig) bool {
if challengeConfig.Challenge.Zip == nil {
return true
}
return *challengeConfig.Challenge.Zip
}

func zipFileName(challengeConfig *ChallengeConfig) string {
return challengeConfig.Challenge.Category + "_" + challengeConfig.Challenge.Slug + ".zip"
}

func getCategoryName(challengeConfig *ChallengeConfig, mappingMap MappingMap) string {
// Get the category from the challenge config
category := challengeConfig.Challenge.Category
Expand Down
98 changes: 65 additions & 33 deletions src/ctfd-challenges.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package main

import (
"archive/zip"
"bytes"
"errors"
"fmt"
"log"
Expand Down Expand Up @@ -142,59 +144,89 @@ func deleteUploadedCTFdChallenge(challengeName string) error {
}

func uploadCTFdChallengeFile(id int, challenge *ChallengeConfig, client *ctfd.Client) (int, error) {
// Get files
files, err := getGithubDirContents(getGithubRepo(), getGithubBranch(), filesDirPath(challenge))
// Get files (recursively, so nested handout subdirectories are included)
remoteFiles, err := getGithubDirContentsRecursive(getGithubRepo(), getGithubBranch(), filesDirPath(challenge))
if err != nil {
log.Printf("Error getting directory contents (err): %s\n", err)
return 0, err
}

filesContent := make([]*ctfd.InputFile, 0)
if files != nil && len(files) > 0 {
for _, file := range files {
if file.GetName() == ".gitignore" || file.GetName() == ".gitkeep" {
continue
}
for _, remoteFile := range remoteFiles {
data, err := getGithubFileBytes(getGithubRepo(), getGithubBranch(), remoteFile.Path)
if err != nil {
log.Printf("Error getting file content: %s\n", err)
continue
}

// Get file content
path := (filesDirPath(challenge) + "/" + file.GetName())
data, error := getGithubFileBytes(getGithubRepo(), getGithubBranch(), path)
if data == nil {
continue
}

if error != nil {
log.Printf("Error getting file content: %s\n", error)
}
filesContent = append(filesContent, &ctfd.InputFile{
Name: remoteFile.RelPath,
Content: []byte(*data),
})
}

// Convert to format
if error == nil && data != nil && file.GetName() != "" {
filesContent = append(filesContent, &ctfd.InputFile{
Name: file.GetName(),
Content: []byte(*data),
})
}
if len(filesContent) == 0 {
return id, nil
}

if shouldZip(challenge) {
zipped, err := zipInputFiles(filesContent)
if err != nil {
log.Printf("Error zipping handout files: %s\n", err)
return 0, err
}
filesContent = []*ctfd.InputFile{
{
Name: zipFileName(challenge),
Content: zipped,
},
}
}

// Print files
if len(filesContent) > 0 {
for _, file := range filesContent {
log.Printf("File: %s\n", file.Name)
// log.Printf("Content: %s\n", string(file.Content))
}
for _, file := range filesContent {
log.Printf("File: %s\n", file.Name)
// log.Printf("Content: %s\n", string(file.Content))
}

// Upload files
if len(filesContent) != 0 {
_, err = client.PostFiles(&ctfd.PostFilesParams{
Files: filesContent,
Challenge: &id,
})
_, err = client.PostFiles(&ctfd.PostFilesParams{
Files: filesContent,
Challenge: &id,
})
if err != nil {
log.Printf("Error uploading files: %s\n", err)
return 0, err
}

return id, nil
}

// zipInputFiles builds an in-memory zip archive containing each of the
// given files at its relative path, preserving directory structure.
func zipInputFiles(files []*ctfd.InputFile) ([]byte, error) {
buf := new(bytes.Buffer)
writer := zip.NewWriter(buf)

for _, file := range files {
entry, err := writer.Create(file.Name)
if err != nil {
log.Printf("Error uploading files: %s\n", err)
return 0, err
return nil, err
}
if _, err := entry.Write(file.Content); err != nil {
return nil, err
}
}

return id, nil
if err := writer.Close(); err != nil {
return nil, err
}

return buf.Bytes(), nil
}

func uploadCTFdChallenge(challenge *ChallengeConfig, client *ctfd.Client) (int, error) {
Expand Down
38 changes: 38 additions & 0 deletions src/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,44 @@ func getGithubDirContents(repo, branch, path string) ([]*github.RepositoryConten
return contents, nil
}

type RemoteFile struct {
// Path is the full path to the file in the repository.
Path string
// RelPath is the path relative to the root directory that was walked.
RelPath string
}
func getGithubDirContentsRecursive(repo, branch, path string) ([]RemoteFile, error) {
entries, err := getGithubDirContents(repo, branch, path)
if err != nil {
return nil, err
}

files := make([]RemoteFile, 0)
for _, entry := range entries {
name := entry.GetName()
if name == "" || name == ".gitignore" || name == ".gitkeep" || name == ".git" {
continue
}

entryPath := path + "/" + name
if entry.GetType() == "dir" {
nested, err := getGithubDirContentsRecursive(repo, branch, entryPath)
if err != nil {
return nil, err
}
files = append(files, nested...)
continue
}

files = append(files, RemoteFile{
Path: entryPath,
RelPath: strings.TrimPrefix(entryPath, path+"/"),
})
Comment on lines +74 to +85
}

return files, nil
}

func getGithubFileBytes(repo, branch, path string) (*string, error) {
owner, repo := splitRepo(repo)

Expand Down
Loading