diff --git a/cmd/ctfd-setup/main.go b/cmd/ctfd-setup/main.go index 4949870..03bd58c 100644 --- a/cmd/ctfd-setup/main.go +++ b/cmd/ctfd-setup/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "net/mail" "os" "os/signal" "path/filepath" @@ -9,7 +10,7 @@ import ( ctfdsetup "github.com/ctfer-io/ctfd-setup" "github.com/pkg/errors" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "go.uber.org/zap" "gopkg.in/yaml.v3" ) @@ -27,7 +28,7 @@ const ( ) func main() { - app := &cli.App{ + app := &cli.Command{ Name: "CTFd Setup", Usage: "Setup (and update) a CTFd instance from a fresh install or an already-existing one.", Flags: []cli.Flag{ @@ -36,26 +37,26 @@ func main() { &cli.StringFlag{ Name: "file", Usage: "Configuration file to use for setting up CTFd. If let empty, will default the values and look for secrets in expected environment variables. For more info, refers to the documentation.", - EnvVars: []string{"FILE", "PLUGIN_FILE"}, + Sources: cli.EnvVars("FILE", "PLUGIN_FILE"), Category: management, }, &cli.StringFlag{ Name: "dir", Usage: "The directory to parse from.", - EnvVars: []string{"DIRECTORY"}, + Sources: cli.EnvVars("DIRECTORY"), Category: management, Destination: &ctfdsetup.Directory, }, &cli.StringFlag{ Name: "url", Usage: "URL to reach the CTFd instance.", - EnvVars: []string{"URL", "PLUGIN_URL"}, + Sources: cli.EnvVars("URL", "PLUGIN_URL"), Category: management, }, &cli.StringFlag{ Name: "api_key", Usage: "The API key to use (for instance for a CI SA), used for updating a running CTFd instance.", - EnvVars: []string{"API_KEY", "PLUGIN_API_KEY"}, + Sources: cli.EnvVars("API_KEY", "PLUGIN_API_KEY"), Category: management, }, // Configuration file @@ -63,145 +64,145 @@ func main() { &cli.StringFlag{ Name: "appearance.name", Usage: "The name of your CTF, displayed as is.", - EnvVars: []string{"APPEARANCE_NAME", "PLUGIN_APPEARANCE_NAME"}, + Sources: cli.EnvVars("APPEARANCE_NAME", "PLUGIN_APPEARANCE_NAME"), Category: configuration, }, &cli.StringFlag{ Name: "appearance.description", Usage: "The description of your CTF, displayed as is.", - EnvVars: []string{"APPEARANCE_DESCRIPTION", "PLUGIN_APPEARANCE_DESCRIPTION"}, + Sources: cli.EnvVars("APPEARANCE_DESCRIPTION", "PLUGIN_APPEARANCE_DESCRIPTION"), Category: configuration, }, &cli.StringFlag{ Name: "appearance.default_locale", Usage: "The default language for the users.", - EnvVars: []string{"APPEARANCE_DEFAULT_LOCALE", "PLUGIN_APPEARANCE_DEFAULT_LOCALE"}, + Sources: cli.EnvVars("APPEARANCE_DEFAULT_LOCALE", "PLUGIN_APPEARANCE_DEFAULT_LOCALE"), Category: configuration, }, // => Theme &cli.StringFlag{ Name: "theme.logo", Usage: "The frontend logo. Provide a path to a locally-accessible file.", - EnvVars: []string{"THEME_LOGO", "PLUGIN_THEME_LOGO"}, + Sources: cli.EnvVars("THEME_LOGO", "PLUGIN_THEME_LOGO"), Category: configuration, }, &cli.StringFlag{ Name: "theme.small_icon", Usage: "The frontend small icon. Provide a path to a locally-accessible file.", - EnvVars: []string{"THEME_SMALL_ICON", "PLUGIN_THEME_SMALL_ICON"}, + Sources: cli.EnvVars("THEME_SMALL_ICON", "PLUGIN_THEME_SMALL_ICON"), Category: configuration, }, &cli.StringFlag{ Name: "theme.name", Usage: "The frontend theme name.", Value: "core-beta", - EnvVars: []string{"THEME_NAME", "PLUGIN_THEME_NAME"}, + Sources: cli.EnvVars("THEME_NAME", "PLUGIN_THEME_NAME"), Category: configuration, }, &cli.StringFlag{ Name: "theme.color", Usage: "The frontend theme color.", - EnvVars: []string{"THEME_COLOR", "PLUGIN_THEME_COLOR"}, + Sources: cli.EnvVars("THEME_COLOR", "PLUGIN_THEME_COLOR"), Category: configuration, }, &cli.StringFlag{ Name: "theme.header", Usage: "The frontend header. Provide a path to a locally-accessible file.", - EnvVars: []string{"THEME_HEADER", "PLUGIN_THEME_HEADER"}, + Sources: cli.EnvVars("THEME_HEADER", "PLUGIN_THEME_HEADER"), Category: configuration, }, &cli.StringFlag{ Name: "theme.footer", Usage: "The frontend footer. Provide a path to a locally-accessible file.", - EnvVars: []string{"THEME_FOOTER", "PLUGIN_THEME_FOOTER"}, + Sources: cli.EnvVars("THEME_FOOTER", "PLUGIN_THEME_FOOTER"), Category: configuration, }, &cli.StringFlag{ Name: "theme.settings", Usage: "The frontend settings (JSON). Provide a path to a locally-accessible file.", - EnvVars: []string{"THEME_SETTINGS", "PLUGIN_THEME_SETTINGS"}, + Sources: cli.EnvVars("THEME_SETTINGS", "PLUGIN_THEME_SETTINGS"), Category: configuration, }, // => Accounts &cli.StringFlag{ Name: "accounts.domain_whitelist", Usage: "The domain whitelist (a list separated by colons) to allow users to have email addresses from.", - EnvVars: []string{"ACCOUNTS_DOMAIN_WHITELIST", "PLUGIN_ACCOUNTS_DOMAIN_WHITELIST"}, + Sources: cli.EnvVars("ACCOUNTS_DOMAIN_WHITELIST", "PLUGIN_ACCOUNTS_DOMAIN_WHITELIST"), Category: configuration, }, &cli.StringFlag{ Name: "accounts.domain_blacklist", Usage: "The domain blacklist (a list separated by colons) to block users to have email addresses from.", - EnvVars: []string{"ACCOUNTS_DOMAIN_BLACKLIST", "PLUGIN_ACCOUNTS_DOMAIN_BLACKLIST"}, + Sources: cli.EnvVars("ACCOUNTS_DOMAIN_BLACKLIST", "PLUGIN_ACCOUNTS_DOMAIN_BLACKLIST"), Category: configuration, }, &cli.BoolFlag{ Name: "accounts.verify_emails", Usage: "Whether to verify emails once a user register or not.", Value: false, - EnvVars: []string{"ACCOUNTS_VERIFY_EMAILS", "PLUGIN_ACCOUNTS_VERIFY_EMAILS"}, + Sources: cli.EnvVars("ACCOUNTS_VERIFY_EMAILS", "PLUGIN_ACCOUNTS_VERIFY_EMAILS"), Category: configuration, }, &cli.BoolFlag{ Name: "accounts.team_creation", Usage: "Whether to allow team creation by players or not.", - EnvVars: []string{"ACCOUNTS_TEAM_CREATION", "PLUGIN_ACCOUNTS_TEAM_CREATION"}, + Sources: cli.EnvVars("ACCOUNTS_TEAM_CREATION", "PLUGIN_ACCOUNTS_TEAM_CREATION"), Category: configuration, }, &cli.IntFlag{ Name: "accounts.team_size", Usage: "Maximum size (number of players) in a team.", - EnvVars: []string{"ACCOUNTS_TEAM_SIZE", "PLUGIN_ACCOUNTS_TEAM_SIZE"}, + Sources: cli.EnvVars("ACCOUNTS_TEAM_SIZE", "PLUGIN_ACCOUNTS_TEAM_SIZE"), Category: configuration, }, &cli.IntFlag{ Name: "accounts.num_teams", Usage: "The total number of teams allowed.", - EnvVars: []string{"ACCOUNTS_NUM_TEAMS", "PLUGIN_ACCOUNTS_NUM_TEAMS"}, + Sources: cli.EnvVars("ACCOUNTS_NUM_TEAMS", "PLUGIN_ACCOUNTS_NUM_TEAMS"), Category: configuration, }, &cli.IntFlag{ Name: "accounts.num_users", Usage: "The total number of users allowed.", - EnvVars: []string{"ACCOUNTS_NUM_USERS", "PLUGIN_ACCOUNTS_NUM_USERS"}, + Sources: cli.EnvVars("ACCOUNTS_NUM_USERS", "PLUGIN_ACCOUNTS_NUM_USERS"), Category: configuration, }, &cli.StringFlag{ Name: "accounts.team_disbanding", Usage: "Whether to allow teams to be disbanded or not. Could be inactive_only or disabled.", - EnvVars: []string{"ACCOUNTS_TEAM_DISBANDING", "PLUGIN_ACCOUNTS_TEAM_DISBANDING"}, + Sources: cli.EnvVars("ACCOUNTS_TEAM_DISBANDING", "PLUGIN_ACCOUNTS_TEAM_DISBANDING"), Category: configuration, }, &cli.IntFlag{ Name: "accounts.incorrect_submissions_per_minute", Usage: "Maximum number of invalid submissions per minute (per user/team). We suggest you use it as part of an anti-brute-force strategy (rate limiting).", - EnvVars: []string{"ACCOUNTS_INCORRECT_SUBMISSIONS_PER_MINUTE", "PLUGIN_ACCOUNTS_INCORRECT_SUBMISSIONS_PER_MINUTE"}, + Sources: cli.EnvVars("ACCOUNTS_INCORRECT_SUBMISSIONS_PER_MINUTE", "PLUGIN_ACCOUNTS_INCORRECT_SUBMISSIONS_PER_MINUTE"), Category: configuration, }, &cli.BoolFlag{ Name: "accounts.name_changes", Usage: "Whether a user can change its name or not.", - EnvVars: []string{"ACCOUNTS_NAME_CHANGES", "PLUGIN_ACCOUNTS_NAME_CHANGES"}, + Sources: cli.EnvVars("ACCOUNTS_NAME_CHANGES", "PLUGIN_ACCOUNTS_NAME_CHANGES"), Category: configuration, }, // => Pages &cli.StringFlag{ Name: "pages.robots_txt", Usage: "Define the /robots.txt file content, for web crawlers indexing. Provide a path to a locally-accessible file.", - EnvVars: []string{"PAGES_ROBOTS_TXT", "PLUGIN_PAGES_ROBOTS_TXT"}, + Sources: cli.EnvVars("PAGES_ROBOTS_TXT", "PLUGIN_PAGES_ROBOTS_TXT"), Category: configuration, }, // => MajorLeagueCyber &cli.StringFlag{ Name: "major_league_cyber.client_id", Usage: "The MajorLeagueCyber OAuth ClientID.", - EnvVars: []string{"MAJOR_LEAGUE_CYBER_CLIENT_ID", "PLUGIN_MAJOR_LEAGUE_CYBER_CLIENT_ID"}, + Sources: cli.EnvVars("MAJOR_LEAGUE_CYBER_CLIENT_ID", "PLUGIN_MAJOR_LEAGUE_CYBER_CLIENT_ID"), Category: configuration, }, &cli.StringFlag{ Name: "major_league_cyber.client_secret", Usage: "The MajorLeagueCyber OAuth Client Secret.", - EnvVars: []string{"MAJOR_LEAGUE_CYBER_CLIENT_SECRET", "PLUGIN_MAJOR_LEAGUE_CYBER_CLIENT_SECRET"}, + Sources: cli.EnvVars("MAJOR_LEAGUE_CYBER_CLIENT_SECRET", "PLUGIN_MAJOR_LEAGUE_CYBER_CLIENT_SECRET"), Category: configuration, }, // => Settings @@ -209,207 +210,207 @@ func main() { Name: "settings.challenge_visibility", Usage: "The visibility for the challenges. Please refer to CTFd documentation (https://docs.ctfd.io/docs/settings/visibility-settings/).", Value: "public", - EnvVars: []string{"SETTINGS_CHALLENGE_VISIBILITY", "PLUGIN_SETTINGS_CHALLENGE_VISIBILITY"}, + Sources: cli.EnvVars("SETTINGS_CHALLENGE_VISIBILITY", "PLUGIN_SETTINGS_CHALLENGE_VISIBILITY"), Category: configuration, }, &cli.StringFlag{ Name: "settings.account_visibility", Usage: "The visibility for the accounts. Please refer to CTFd documentation (https://docs.ctfd.io/docs/settings/visibility-settings/).", Value: "public", - EnvVars: []string{"SETTINGS_ACCOUNT_VISIBILITY", "PLUGIN_SETTINGS_ACCOUNT_VISIBILITY"}, + Sources: cli.EnvVars("SETTINGS_ACCOUNT_VISIBILITY", "PLUGIN_SETTINGS_ACCOUNT_VISIBILITY"), Category: configuration, }, &cli.StringFlag{ Name: "settings.score_visibility", Usage: "The visibility for the scoreboard. Please refer to CTFd documentation (https://docs.ctfd.io/docs/settings/visibility-settings/).", Value: "public", - EnvVars: []string{"SETTINGS_SCORE_VISIBILITY", "PLUGIN_SETTINGS_SCORE_VISIBILITY"}, + Sources: cli.EnvVars("SETTINGS_SCORE_VISIBILITY", "PLUGIN_SETTINGS_SCORE_VISIBILITY"), Category: configuration, }, &cli.StringFlag{ Name: "settings.registration_visibility", Usage: "The visibility for the registration. Please refer to CTFd documentation (https://docs.ctfd.io/docs/settings/visibility-settings/).", Value: "public", - EnvVars: []string{"SETTINGS_REGISTRATION_VISIBILITY", "PLUGIN_SETTINGS_REGISTRATION_VISIBILITY"}, + Sources: cli.EnvVars("SETTINGS_REGISTRATION_VISIBILITY", "PLUGIN_SETTINGS_REGISTRATION_VISIBILITY"), Category: configuration, }, &cli.BoolFlag{ Name: "settings.paused", Usage: "Whether the CTFd is paused or not.", - EnvVars: []string{"SETTINGS_PAUSED", "PLUGIN_SETTINGS_PAUSED"}, + Sources: cli.EnvVars("SETTINGS_PAUSED", "PLUGIN_SETTINGS_PAUSED"), Category: configuration, }, // => Security &cli.BoolFlag{ Name: "security.html_sanitization", Usage: "Whether to turn on HTML sanitization or not.", - EnvVars: []string{"SECURITY_HTML_SANITIZATION", "PLUGIN_SECURITY_HTML_SANITIZATION"}, + Sources: cli.EnvVars("SECURITY_HTML_SANITIZATION", "PLUGIN_SECURITY_HTML_SANITIZATION"), Category: configuration, }, &cli.StringFlag{ Name: "security.registration_code", Usage: "The registration code (secret) to join the CTF.", - EnvVars: []string{"SECURITY_REGISTRATION_CODE", "PLUGIN_SECURITY_REGISTRATION_CODE"}, + Sources: cli.EnvVars("SECURITY_REGISTRATION_CODE", "PLUGIN_SECURITY_REGISTRATION_CODE"), Category: configuration, }, // => Email &cli.StringFlag{ Name: "email.registration.subject", Usage: "The email registration subject of the mail.", - EnvVars: []string{"EMAIL_REGISTRATION_SUBJECT", "PLUGIN_EMAIL_REGISTRATION_SUBJECT"}, + Sources: cli.EnvVars("EMAIL_REGISTRATION_SUBJECT", "PLUGIN_EMAIL_REGISTRATION_SUBJECT"), Category: configuration, }, &cli.StringFlag{ Name: "email.registration.body", Usage: "The email registration body of the mail.", - EnvVars: []string{"EMAIL_REGISTRATION_BODY", "PLUGIN_EMAIL_REGISTRATION_BODY"}, + Sources: cli.EnvVars("EMAIL_REGISTRATION_BODY", "PLUGIN_EMAIL_REGISTRATION_BODY"), Category: configuration, }, &cli.StringFlag{ Name: "email.confirmation.subject", Usage: "The email confirmation subject of the mail.", - EnvVars: []string{"EMAIL_CONFIRMATION_SUBJECT", "PLUGIN_EMAIL_CONFIRMATION_SUBJECT"}, + Sources: cli.EnvVars("EMAIL_CONFIRMATION_SUBJECT", "PLUGIN_EMAIL_CONFIRMATION_SUBJECT"), Category: configuration, }, &cli.StringFlag{ Name: "email.confirmation.body", Usage: "The email confirmation body of the mail.", - EnvVars: []string{"EMAIL_CONFIRMATION_BODY", "PLUGIN_EMAIL_CONFIRMATION_BODY"}, + Sources: cli.EnvVars("EMAIL_CONFIRMATION_BODY", "PLUGIN_EMAIL_CONFIRMATION_BODY"), Category: configuration, }, &cli.StringFlag{ Name: "email.new_account.subject", Usage: "The email new_account subject of the mail.", - EnvVars: []string{"EMAIL_NEW_ACCOUNT_SUBJECT", "PLUGIN_EMAIL_NEW_ACCOUNT_SUBJECT"}, + Sources: cli.EnvVars("EMAIL_NEW_ACCOUNT_SUBJECT", "PLUGIN_EMAIL_NEW_ACCOUNT_SUBJECT"), Category: configuration, }, &cli.StringFlag{ Name: "email.new_account.body", Usage: "The email new_account body of the mail.", - EnvVars: []string{"EMAIL_NEW_ACCOUNT_BODY", "PLUGIN_EMAIL_NEW_ACCOUNT_BODY"}, + Sources: cli.EnvVars("EMAIL_NEW_ACCOUNT_BODY", "PLUGIN_EMAIL_NEW_ACCOUNT_BODY"), Category: configuration, }, &cli.StringFlag{ Name: "email.password_reset.subject", Usage: "The email password_reset subject of the mail.", - EnvVars: []string{"EMAIL_PASSWORD_RESET_SUBJECT", "PLUGIN_EMAIL_PASSWORD_RESET_SUBJECT"}, + Sources: cli.EnvVars("EMAIL_PASSWORD_RESET_SUBJECT", "PLUGIN_EMAIL_PASSWORD_RESET_SUBJECT"), Category: configuration, }, &cli.StringFlag{ Name: "email.password_reset.body", Usage: "The email password_reset body of the mail.", - EnvVars: []string{"EMAIL_PASSWORD_RESET_BODY", "PLUGIN_EMAIL_PASSWORD_RESET_BODY"}, + Sources: cli.EnvVars("EMAIL_PASSWORD_RESET_BODY", "PLUGIN_EMAIL_PASSWORD_RESET_BODY"), Category: configuration, }, &cli.StringFlag{ Name: "email.password_reset_confirmation.subject", Usage: "The email password_reset_confirmation subject of the mail.", - EnvVars: []string{"EMAIL_PASSWORD_RESET_CONFIRMATION_SUBJECT", "PLUGIN_EMAIL_PASSWORD_RESET_CONFIRMATION_SUBJECT"}, + Sources: cli.EnvVars("EMAIL_PASSWORD_RESET_CONFIRMATION_SUBJECT", "PLUGIN_EMAIL_PASSWORD_RESET_CONFIRMATION_SUBJECT"), Category: configuration, }, &cli.StringFlag{ Name: "email.password_reset_confirmation.body", Usage: "The email password_reset_confirmation body of the mail.", - EnvVars: []string{"EMAIL_PASSWORD_RESET_CONFIRMATION_BODY", "PLUGIN_EMAIL_PASSWORD_RESET_CONFIRMATION_BODY"}, + Sources: cli.EnvVars("EMAIL_PASSWORD_RESET_CONFIRMATION_BODY", "PLUGIN_EMAIL_PASSWORD_RESET_CONFIRMATION_BODY"), Category: configuration, }, &cli.StringFlag{ Name: "email.from", Usage: "The 'From:' to sent to mail with.", - EnvVars: []string{"EMAIL_MAIL_FROM", "PLUGIN_EMAIL_MAIL_FROM"}, + Sources: cli.EnvVars("EMAIL_MAIL_FROM", "PLUGIN_EMAIL_MAIL_FROM"), Category: configuration, }, &cli.StringFlag{ Name: "email.server", Usage: "The mail server to use.", - EnvVars: []string{"EMAIL_MAIL_SERVER", "PLUGIN_EMAIL_MAIL_SERVER"}, + Sources: cli.EnvVars("EMAIL_MAIL_SERVER", "PLUGIN_EMAIL_MAIL_SERVER"), Category: configuration, }, &cli.StringFlag{ Name: "email.port", Usage: "The mail server port to reach.", - EnvVars: []string{"EMAIL_MAIL_SERVER_PORT", "PLUGIN_EMAIL_MAIL_SERVER_PORT"}, + Sources: cli.EnvVars("EMAIL_MAIL_SERVER_PORT", "PLUGIN_EMAIL_MAIL_SERVER_PORT"), Category: configuration, }, &cli.StringFlag{ Name: "email.username", Usage: "The username to log in to the mail server.", - EnvVars: []string{"EMAIL_USERNAME", "PLUGIN_EMAIL_USERNAME"}, + Sources: cli.EnvVars("EMAIL_USERNAME", "PLUGIN_EMAIL_USERNAME"), Category: configuration, }, &cli.StringFlag{ Name: "email.password", Usage: "The password to log in to the mail server.", - EnvVars: []string{"EMAIL_PASSWORD", "PLUGIN_EMAIL_PASSWORD"}, + Sources: cli.EnvVars("EMAIL_PASSWORD", "PLUGIN_EMAIL_PASSWORD"), Category: configuration, }, &cli.BoolFlag{ Name: "email.tls_ssl", Usage: "Whether to turn on TLS/SSL or not.", - EnvVars: []string{"EMAIL_TLS_SSL", "PLUGIN_EMAIL_TLS_SSL"}, + Sources: cli.EnvVars("EMAIL_TLS_SSL", "PLUGIN_EMAIL_TLS_SSL"), Category: configuration, }, &cli.BoolFlag{ Name: "email.starttls", Usage: "Whether to turn on STARTTLS or not.", - EnvVars: []string{"EMAIL_STARTTLS", "PLUGIN_EMAIL_STARTTLS"}, + Sources: cli.EnvVars("EMAIL_STARTTLS", "PLUGIN_EMAIL_STARTTLS"), Category: configuration, }, // => Time &cli.StringFlag{ Name: "time.start", Usage: "The start timestamp at which the CTFd will open.", - EnvVars: []string{"TIME_START", "PLUGIN_TIME_START"}, + Sources: cli.EnvVars("TIME_START", "PLUGIN_TIME_START"), Category: configuration, }, &cli.StringFlag{ Name: "time.end", Usage: "The end timestamp at which the CTFd will close.", - EnvVars: []string{"TIME_END", "PLUGIN_TIME_END"}, + Sources: cli.EnvVars("TIME_END", "PLUGIN_TIME_END"), Category: configuration, }, &cli.StringFlag{ Name: "time.freeze", Usage: "The freeze timestamp at which the CTFd will remain open but won't accept any further submissions.", - EnvVars: []string{"TIME_FREEZE", "PLUGIN_TIME_FREEZE"}, + Sources: cli.EnvVars("TIME_FREEZE", "PLUGIN_TIME_FREEZE"), Category: configuration, }, &cli.BoolFlag{ Name: "time.view_after", Usage: "Whether allows users to view challenges after end or not.", - EnvVars: []string{"TIME_VIEW_AFTER", "PLUGIN_TIME_VIEW_AFTER"}, + Sources: cli.EnvVars("TIME_VIEW_AFTER", "PLUGIN_TIME_VIEW_AFTER"), Category: configuration, }, // => Social &cli.BoolFlag{ Name: "social.shares", Usage: "Whether to enable users share they solved a challenge or not.", - EnvVars: []string{"SOCIAL_SHARES", "PLUGIN_SOCIAL_SHARES"}, + Sources: cli.EnvVars("SOCIAL_SHARES", "PLUGIN_SOCIAL_SHARES"), Category: configuration, }, // => Legal &cli.StringFlag{ Name: "legal.tos.url", Usage: "The Terms of Services URL.", - EnvVars: []string{"LEGAL_TOS_URL", "PLUGIN_LEGAL_TOS_URL"}, + Sources: cli.EnvVars("LEGAL_TOS_URL", "PLUGIN_LEGAL_TOS_URL"), Category: configuration, }, &cli.StringFlag{ Name: "legal.tos.content", Usage: "The Terms of Services content.", - EnvVars: []string{"LEGAL_TOS_CONTENT", "PLUGIN_LEGAL_TOS_CONTENT"}, + Sources: cli.EnvVars("LEGAL_TOS_CONTENT", "PLUGIN_LEGAL_TOS_CONTENT"), Category: configuration, }, &cli.StringFlag{ Name: "legal.privacy_policy.url", Usage: "The Privacy Policy URL.", - EnvVars: []string{"LEGAL_PRIVACY_POLICY_URL", "PLUGIN_LEGAL_PRIVACY_POLICY_URL"}, + Sources: cli.EnvVars("LEGAL_PRIVACY_POLICY_URL", "PLUGIN_LEGAL_PRIVACY_POLICY_URL"), Category: configuration, }, &cli.StringFlag{ Name: "legal.privacy_policy.content", Usage: "The Privacy Policy content.", - EnvVars: []string{"LEGAL_PRIVACY_POLICY_CONTENT", "PLUGIN_LEGAL_PRIVACY_POLICY_CONTENT"}, + Sources: cli.EnvVars("LEGAL_PRIVACY_POLICY_CONTENT", "PLUGIN_LEGAL_PRIVACY_POLICY_CONTENT"), Category: configuration, }, // => UserMode @@ -417,26 +418,26 @@ func main() { Name: "mode", Usage: "The mode of your CTFd, either users or teams.", Value: "users", - EnvVars: []string{"MODE", "PLUGIN_MODE"}, + Sources: cli.EnvVars("MODE", "PLUGIN_MODE"), Category: configuration, }, // => admin &cli.StringFlag{ Name: "admin.name", Usage: "The administrator name. Immutable, or need the administrator to change the CTFd data AND the configuration (CLI, varenv, file). Required.", - EnvVars: []string{"ADMIN_NAME", "PLUGIN_ADMIN_NAME"}, + Sources: cli.EnvVars("ADMIN_NAME", "PLUGIN_ADMIN_NAME"), Category: configuration, }, &cli.StringFlag{ Name: "admin.email", Usage: "The administrator email address. Immutable, or need the administrator to change the CTFd data AND the configuration (CLI, varenv, file). Required.", - EnvVars: []string{"ADMIN_EMAIL", "PLUGIN_ADMIN_EMAIL"}, + Sources: cli.EnvVars("ADMIN_EMAIL", "PLUGIN_ADMIN_EMAIL"), Category: configuration, }, &cli.StringFlag{ Name: "admin.password", Usage: "The administrator password, recommended to use the varenvs. Immutable, or need the administrator to change the CTFd data AND the configuration (CLI, varenv, file). Required.", - EnvVars: []string{"ADMIN_PASSWORD", "PLUGIN_ADMIN_PASSWORD"}, + Sources: cli.EnvVars("ADMIN_PASSWORD", "PLUGIN_ADMIN_PASSWORD"), Category: configuration, }, }, @@ -452,8 +453,8 @@ func main() { Value: "schema.json", }, }, - Action: func(ctx *cli.Context) error { - o := ctx.String("output") + Action: func(ctx context.Context, cmd *cli.Command) error { + o := cmd.String("output") schema, err := ctfdsetup.Config{}.Schema() if err != nil { return err @@ -463,10 +464,10 @@ func main() { }, }, Action: run, - Authors: []*cli.Author{ - { - Name: "Lucas Tesson - PandatiX", - Email: "lucastesson@protonmail.com", + Authors: []any{ + mail.Address{ + Name: "Lucas Tesson - PandatiX", + Address: "lucastesson@protonmail.com", }, }, Version: version, @@ -482,150 +483,150 @@ func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() - if err := app.RunContext(ctx, os.Args); err != nil { + if err := app.Run(ctx, os.Args); err != nil { ctfdsetup.Log().Error("fatal error", zap.Error(err)) os.Exit(1) } } -func run(ctx *cli.Context) error { +func run(ctx context.Context, cmd *cli.Command) error { log := ctfdsetup.Log() - logo, err := filePtr(ctx, "theme.logo") + logo, err := filePtr(cmd, "theme.logo") if err != nil { return err } - smallIcon, err := filePtr(ctx, "theme.small_icon") + smallIcon, err := filePtr(cmd, "theme.small_icon") if err != nil { return err } - header, err := filePtr(ctx, "theme.header") + header, err := filePtr(cmd, "theme.header") if err != nil { return err } - footer, err := filePtr(ctx, "theme.footer") + footer, err := filePtr(cmd, "theme.footer") if err != nil { return err } - settings, err := filePtr(ctx, "theme.settings") + settings, err := filePtr(cmd, "theme.settings") if err != nil { return err } - robotsTxt, err := filePtr(ctx, "pages.robots_txt") + robotsTxt, err := filePtr(cmd, "pages.robots_txt") if err != nil { return err } - tos, err := filePtr(ctx, "legal.tos.content") + tos, err := filePtr(cmd, "legal.tos.content") if err != nil { return err } - privpol, err := filePtr(ctx, "legal.privacy_policy.content") + privpol, err := filePtr(cmd, "legal.privacy_policy.content") if err != nil { return err } conf := &ctfdsetup.Config{ Appearance: ctfdsetup.Appearance{ - Name: ctx.String("appearance.name"), - Description: ctx.String("appearance.description"), - DefaultLocale: stringPtr(ctx, "appearance.default_locale"), + Name: cmd.String("appearance.name"), + Description: cmd.String("appearance.description"), + DefaultLocale: stringPtr(cmd, "appearance.default_locale"), }, Theme: &ctfdsetup.Theme{ Logo: logo, SmallIcon: smallIcon, - Name: ctx.String("theme.name"), - Color: ctx.String("theme.color"), + Name: cmd.String("theme.name"), + Color: cmd.String("theme.color"), Header: header, Footer: footer, Settings: settings, }, Accounts: &ctfdsetup.Accounts{ - DomainWhitelist: stringPtr(ctx, "accounts.domain_whitelist"), - DomainBlacklist: stringPtr(ctx, "accounts.domain_blacklist"), - VerifyEmails: ctx.Bool("accounts.verify_emails"), - TeamCreation: boolPtr(ctx, "accounts.team_creation"), - TeamSize: intPtr(ctx, "accounts.team_size"), - NumTeams: intPtr(ctx, "accounts.num_teams"), - NumUsers: intPtr(ctx, "accounts.num_users"), - TeamDisbanding: stringPtr(ctx, "accounts.team_disbanding"), - IncorrectSubmissionsPerMinute: intPtr(ctx, "accounts.incorrect_submissions_per_minute"), - NameChanges: boolPtr(ctx, "accounts.name_changes"), + DomainWhitelist: stringPtr(cmd, "accounts.domain_whitelist"), + DomainBlacklist: stringPtr(cmd, "accounts.domain_blacklist"), + VerifyEmails: cmd.Bool("accounts.verify_emails"), + TeamCreation: boolPtr(cmd, "accounts.team_creation"), + TeamSize: intPtr(cmd, "accounts.team_size"), + NumTeams: intPtr(cmd, "accounts.num_teams"), + NumUsers: intPtr(cmd, "accounts.num_users"), + TeamDisbanding: stringPtr(cmd, "accounts.team_disbanding"), + IncorrectSubmissionsPerMinute: intPtr(cmd, "accounts.incorrect_submissions_per_minute"), + NameChanges: boolPtr(cmd, "accounts.name_changes"), }, Pages: &ctfdsetup.Pages{ RobotsTxt: robotsTxt, }, MajorLeagueCyber: &ctfdsetup.MajorLeagueCyber{ - ClientID: stringPtr(ctx, "major_league_cyber.client_id"), - ClientSecret: stringPtr(ctx, "major_league_cyber.client_secret"), + ClientID: stringPtr(cmd, "major_league_cyber.client_id"), + ClientSecret: stringPtr(cmd, "major_league_cyber.client_secret"), }, Settings: &ctfdsetup.Settings{ - ChallengeVisibility: ctx.String("settings.challenge_visibility"), - AccountVisibility: ctx.String("settings.account_visibility"), - ScoreVisibility: ctx.String("settings.score_visibility"), - RegistrationVisibility: ctx.String("settings.registration_visibility"), - Paused: boolPtr(ctx, "settings.paused"), + ChallengeVisibility: cmd.String("settings.challenge_visibility"), + AccountVisibility: cmd.String("settings.account_visibility"), + ScoreVisibility: cmd.String("settings.score_visibility"), + RegistrationVisibility: cmd.String("settings.registration_visibility"), + Paused: boolPtr(cmd, "settings.paused"), }, Security: &ctfdsetup.Security{ - HTMLSanitization: boolPtr(ctx, "security.html_sanitization"), - RegistrationCode: stringPtr(ctx, "security.registration_code"), + HTMLSanitization: boolPtr(cmd, "security.html_sanitization"), + RegistrationCode: stringPtr(cmd, "security.registration_code"), }, Email: &ctfdsetup.Email{ Registration: ctfdsetup.EmailContent{ - Subject: stringPtr(ctx, "email.registration.subject"), - Body: stringPtr(ctx, "email.registration.body"), + Subject: stringPtr(cmd, "email.registration.subject"), + Body: stringPtr(cmd, "email.registration.body"), }, Confirmation: ctfdsetup.EmailContent{ - Subject: stringPtr(ctx, "email.confirmation.subject"), - Body: stringPtr(ctx, "email.confirmation.body"), + Subject: stringPtr(cmd, "email.confirmation.subject"), + Body: stringPtr(cmd, "email.confirmation.body"), }, NewAccount: ctfdsetup.EmailContent{ - Subject: stringPtr(ctx, "email.new_account.subject"), - Body: stringPtr(ctx, "email.new_account.body"), + Subject: stringPtr(cmd, "email.new_account.subject"), + Body: stringPtr(cmd, "email.new_account.body"), }, PasswordReset: ctfdsetup.EmailContent{ - Subject: stringPtr(ctx, "email.password_reset.subject"), - Body: stringPtr(ctx, "email.password_reset.body"), + Subject: stringPtr(cmd, "email.password_reset.subject"), + Body: stringPtr(cmd, "email.password_reset.body"), }, PasswordResetConfirmation: ctfdsetup.EmailContent{ - Subject: stringPtr(ctx, "email.password_reset_confirmation.subject"), - Body: stringPtr(ctx, "email.password_reset_confirmation.body"), - }, - From: stringPtr(ctx, "email.mail_from"), - Server: stringPtr(ctx, "email.mail_server"), - Port: stringPtr(ctx, "email.mail_server_port"), - Username: stringPtr(ctx, "email.username"), - Password: stringPtr(ctx, "email.password"), + Subject: stringPtr(cmd, "email.password_reset_confirmation.subject"), + Body: stringPtr(cmd, "email.password_reset_confirmation.body"), + }, + From: stringPtr(cmd, "email.mail_from"), + Server: stringPtr(cmd, "email.mail_server"), + Port: stringPtr(cmd, "email.mail_server_port"), + Username: stringPtr(cmd, "email.username"), + Password: stringPtr(cmd, "email.password"), }, Time: &ctfdsetup.Time{ - Start: stringPtr(ctx, "time.start"), - End: stringPtr(ctx, "time.end"), - Freeze: stringPtr(ctx, "time.freeze"), - ViewAfter: boolPtr(ctx, "time.view_after"), + Start: stringPtr(cmd, "time.start"), + End: stringPtr(cmd, "time.end"), + Freeze: stringPtr(cmd, "time.freeze"), + ViewAfter: boolPtr(cmd, "time.view_after"), }, Social: &ctfdsetup.Social{ - Shares: boolPtr(ctx, "social.shares"), + Shares: boolPtr(cmd, "social.shares"), }, Legal: &ctfdsetup.Legal{ TOS: ctfdsetup.ExternalReference{ - URL: stringPtr(ctx, "legal.tos.url"), + URL: stringPtr(cmd, "legal.tos.url"), Content: tos, }, PrivacyPolicy: ctfdsetup.ExternalReference{ - URL: stringPtr(ctx, "legal.privacy_policy.url"), + URL: stringPtr(cmd, "legal.privacy_policy.url"), Content: privpol, }, }, - Mode: ctx.String("mode"), + Mode: cmd.String("mode"), Admin: ctfdsetup.Admin{ - Name: ctx.String("admin.name"), - Email: ctx.String("admin.email"), + Name: cmd.String("admin.name"), + Email: cmd.String("admin.email"), Password: ctfdsetup.FromEnv{ - Content: ctx.String("admin.password"), + Content: cmd.String("admin.password"), }, }, } // Read and unmarshal setup config file if any - if f := ctx.String("file"); f != "" { + if f := cmd.String("file"); f != "" { log.Info("loading configuration file", zap.String("file", f)) fd, err := os.Open(f) @@ -648,26 +649,26 @@ func run(ctx *cli.Context) error { } // Connect to CTFd - if !ctx.IsSet("url") { + if !cmd.IsSet("url") { return errors.New("url flag not set, is required") } - return ctfdsetup.Setup(ctx.Context, ctx.String("url"), ctx.String("api_key"), conf) + return ctfdsetup.Setup(ctx, cmd.String("url"), cmd.String("api_key"), conf) } -func stringPtr(ctx *cli.Context, key string) *string { - return genPtr(ctx, key, ctx.String) +func stringPtr(cmd *cli.Command, key string) *string { + return genPtr(cmd, key, cmd.String) } -func boolPtr(ctx *cli.Context, key string) *bool { - return genPtr(ctx, key, ctx.Bool) +func boolPtr(cmd *cli.Command, key string) *bool { + return genPtr(cmd, key, cmd.Bool) } -func intPtr(ctx *cli.Context, key string) *int { - return genPtr(ctx, key, ctx.Int) +func intPtr(cmd *cli.Command, key string) *int { + return genPtr(cmd, key, cmd.Int) } -func filePtr(ctx *cli.Context, key string) (*ctfdsetup.File, error) { - fp := ctx.String(key) +func filePtr(cmd *cli.Command, key string) (*ctfdsetup.File, error) { + fp := cmd.String(key) if fp == "" { return &ctfdsetup.File{}, nil } @@ -681,8 +682,8 @@ func filePtr(ctx *cli.Context, key string) (*ctfdsetup.File, error) { }, nil } -func genPtr[T string | int | bool](ctx *cli.Context, key string, f func(key string) T) *T { - if ctx.IsSet(key) { +func genPtr[T string | int | bool](cmd *cli.Command, key string, f func(key string) T) *T { + if cmd.IsSet(key) { return nil } v := f(key) diff --git a/go.mod b/go.mod index 56f5ccb..df70d2c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/invopop/jsonschema v0.13.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.10.0 - github.com/urfave/cli/v2 v2.27.7 + github.com/urfave/cli/v3 v3.3.8 github.com/xeipuuv/gojsonschema v1.2.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 @@ -17,14 +17,11 @@ require ( require ( github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/gorilla/schema v1.4.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect ) diff --git a/go.sum b/go.sum index 30caf02..f8f57be 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPn github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= -github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/ctfer-io/go-ctfd v0.13.3 h1:SOQhMCKbEAiOMqFZyACbzJoKWg4EtTgpmO9w3gl+ZPs= github.com/ctfer-io/go-ctfd v0.13.3/go.mod h1:ebgSW8LdP/qtRCpglK4djBp+g6kU5YM98XBZKowiCeY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -20,14 +18,12 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= -github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= +github.com/urfave/cli/v3 v3.3.8 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E= +github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= @@ -36,8 +32,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=