Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
28 changes: 24 additions & 4 deletions src/configparser/challenge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ pub fn parse_one(path: &PathBuf) -> Result<ChallengeConfig> {
.merge(Serialized::default("category", category))
.extract()?;

let config = get_config()?;

// coerce pod env lists to maps
// TODO: do this in serde deserialize?
for pod_type in parsed.pods.iter_mut() {
Expand Down Expand Up @@ -105,9 +107,18 @@ pub fn parse_one(path: &PathBuf) -> Result<ChallengeConfig> {
ListOrMap::Map(map)
}
};

// set default resources from global config
if pod.resources.is_none() {
pod.resources = Some(config.defaults.resources.clone())
}
}
}

if parsed.point_class.is_none() {
parsed.point_class = Some(config.defaults.point_class.to_string())
}

trace!("got challenge config: {parsed:#?}");

Ok(parsed)
Expand Down Expand Up @@ -249,10 +260,19 @@ impl ChallengeConfig {
#[fully_pub]
enum FlagType {
RawString(String),
File { file: PathBuf },
Text { text: String },
Regex { regex: String },
Verifier { verifier: String },
File {
file: PathBuf,
},
String {
#[serde(alias = "text")]
string: String,
},
Regex {
regex: String,
},
Verifier {
verifier: String,
},
}

// Parse each distinct kind of Provide action as a separate enum variant
Expand Down
2 changes: 1 addition & 1 deletion src/configparser/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ struct UserPass {
pass: String,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
#[fully_pub]
struct Resource {
cpu: i64,
Expand Down
2 changes: 1 addition & 1 deletion src/deploy/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pub async fn render_frontend_info(
.read_to_string(&mut flag);
flag
}
FlagType::Text { text } => text.clone(),
FlagType::String { string: text } => text.clone(),
FlagType::Regex { regex } => unimplemented!("flag regex not implemented"),
FlagType::Verifier { verifier } => unimplemented!("flag custom verifier not implemented"),
};
Expand Down
129 changes: 120 additions & 9 deletions src/tests/parsing/challenges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@ use std::path::PathBuf;
use std::str::FromStr;

use crate::configparser::challenge::*;
use crate::configparser::config::Resource;

const VALID_CONFIG: &str = r#"
flag_regex: ctf{[a-zA-Z_\-0-9]*}

registry:
domain: images.example.ctf
build: { user: "", pass: "" }
cluster: { user: "", pass: "" }

defaults:
point_class: example
resources: { cpu: 1, memory: 200Mi }

point_classes:
- name: example
min: 0
max: 1337

deploy: {}
profiles: {}
"#;

const VALID_CHAL: &str = r#"
name: testchal
Expand All @@ -26,6 +48,7 @@ const VALID_CHAL: &str = r#"
/// No challenge files should parse correctly
fn no_challenges() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let chals = parse_all();

assert!(chals.is_ok());
Expand All @@ -39,6 +62,7 @@ fn no_challenges() {
/// Challenge yaml at repo root should not parse
fn challenge_in_root() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
jail.create_file("challenge.yaml", "name: test")?;

let chals = parse_all();
Expand All @@ -54,6 +78,7 @@ fn challenge_in_root() {
/// Challenge yaml one folder down should not parse
fn challenge_one_level() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo")?;
jail.create_file(dir.join("challenge.yaml"), "name: test")?;

Expand All @@ -70,6 +95,7 @@ fn challenge_one_level() {
/// Challenge yaml two folders down should be parsed
fn challenge_two_levels() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo/test")?;
jail.create_file(dir.join("challenge.yaml"), VALID_CHAL)?;

Expand All @@ -91,8 +117,8 @@ fn challenge_two_levels() {
directory: PathBuf::from("foo/test"),
challenge_id: "asdf".to_string(),

flag: FlagType::Text {
text: "test{it-works}".to_string()
flag: FlagType::String {
string: "test{it-works}".to_string()
},

provide: vec![],
Expand All @@ -108,6 +134,7 @@ fn challenge_two_levels() {
/// Challenge yaml three folders down should not parsed
fn challenge_three_levels() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("chals/foo/test")?;
jail.create_file(dir.join("challenge.yaml"), VALID_CHAL)?;

Expand All @@ -124,6 +151,7 @@ fn challenge_three_levels() {
fn challenge_no_flag() {
figment::Jail::expect_with(|jail| {
let dir = jail.create_dir("test/noflag")?;
jail.create_file("rcds.yaml", VALID_CONFIG)?;
jail.create_file(
dir.join("challenge.yaml"),
r#"
Expand Down Expand Up @@ -245,6 +273,7 @@ fn challenge_no_id() {
/// Challenges can omit both provides and pods fields if needed
fn challenge_no_provides_or_pods() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo/test")?;
jail.create_file(
dir.join("challenge.yaml"),
Expand Down Expand Up @@ -273,6 +302,7 @@ fn challenge_no_provides_or_pods() {
/// Challenges can omit point_class to use the default at deploy
fn challenge_no_point_class() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo/test")?;
jail.create_file(
dir.join("challenge.yaml"),
Expand All @@ -290,7 +320,7 @@ fn challenge_no_point_class() {

let chals = parse_all().unwrap();

assert_eq!(chals[0].point_class, None);
assert_eq!(chals[0].point_class, Some("example".to_string()));

Ok(())
})
Expand All @@ -300,6 +330,7 @@ fn challenge_no_point_class() {
/// Challenge provide files parse correctly
fn challenge_provide() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo/test")?;
jail.create_file(
dir.join("challenge.yaml"),
Expand Down Expand Up @@ -388,6 +419,7 @@ fn challenge_provide() {
/// Challenge provide files dont parse if include is missing from object form
fn challenge_provide_no_include() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo/test")?;
jail.create_file(
dir.join("challenge.yaml"),
Expand Down Expand Up @@ -425,6 +457,7 @@ fn challenge_provide_no_include() {
/// Challenges should be able to have multiple pods
fn challenge_pods() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo/test")?;
jail.create_file(
dir.join("challenge.yaml"),
Expand Down Expand Up @@ -467,7 +500,10 @@ fn challenge_pods() {
image_source: ImageSource::Image("nginx".to_string()),
architecture: "amd64".to_string(),
env: ListOrMap::Map(HashMap::new()),
resources: None,
resources: Some(Resource {
cpu: 1,
memory: "200Mi".to_string()
}),
replicas: 2,
ports: vec![PortConfig {
internal: 80,
Expand All @@ -484,7 +520,10 @@ fn challenge_pods() {
}),
architecture: "amd64".to_string(),
env: ListOrMap::Map(HashMap::new()),
resources: None,
resources: Some(Resource {
cpu: 1,
memory: "200Mi".to_string()
}),
replicas: 1,
ports: vec![PortConfig {
internal: 8000,
Expand All @@ -503,6 +542,7 @@ fn challenge_pods() {
/// Challenge pods can use simple or complex build options
fn challenge_pod_build() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo/test")?;
jail.create_file(
dir.join("challenge.yaml"),
Expand Down Expand Up @@ -554,7 +594,10 @@ fn challenge_pod_build() {
}),
architecture: "amd64".to_string(),
env: ListOrMap::Map(HashMap::new()),
resources: None,
resources: Some(Resource {
cpu: 1,
memory: "200Mi".to_string()
}),
replicas: 1,
ports: vec![PortConfig {
internal: 80,
Expand All @@ -574,7 +617,10 @@ fn challenge_pod_build() {
}),
architecture: "amd64".to_string(),
env: ListOrMap::Map(HashMap::new()),
resources: None,
resources: Some(Resource {
cpu: 1,
memory: "200Mi".to_string()
}),
replicas: 1,
ports: vec![PortConfig {
internal: 80,
Expand All @@ -593,6 +639,7 @@ fn challenge_pod_build() {
/// Challenge pod envvars can be set as either string list or map
fn challenge_pod_env() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo/test")?;
jail.create_file(
dir.join("challenge.yaml"),
Expand Down Expand Up @@ -644,7 +691,10 @@ fn challenge_pod_env() {
("FOO".to_string(), "this".to_string()),
("BAR".to_string(), "that".to_string()),
])),
resources: None,
resources: Some(Resource {
cpu: 1,
memory: "200Mi".to_string()
}),
replicas: 1,
ports: vec![PortConfig {
internal: 80,
Expand All @@ -660,7 +710,10 @@ fn challenge_pod_env() {
("FOO".to_string(), "this".to_string()),
("BAR".to_string(), "that".to_string()),
])),
resources: None,
resources: Some(Resource {
cpu: 1,
memory: "200Mi".to_string()
}),
replicas: 1,
ports: vec![PortConfig {
internal: 80,
Expand All @@ -679,6 +732,7 @@ fn challenge_pod_env() {
/// Challenge pod envvar strings error if malformed
fn challenge_pod_bad_env() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo/test")?;
jail.create_file(
dir.join("challenge.yaml"),
Expand Down Expand Up @@ -896,3 +950,60 @@ fn challenge_pod_bad_manifest_image() {
Ok(())
})
}

#[test]
/// Challenge pod resources override config defaults
fn challenge_pod_resources() {
figment::Jail::expect_with(|jail| {
jail.create_file("rcds.yaml", VALID_CONFIG)?;
let dir = jail.create_dir("foo/test")?;
jail.create_file(
dir.join("challenge.yaml"),
r#"
name: testchal
author: nobody
description: just a test challenge
challenge_id: asdf

flag:
text: test{it-works}

pods:
- name: foo
image: nginx
replicas: 1
resources:
cpu: 4
memory: 1Gi
ports:
- internal: 80
expose:
http: test.chals.example.com
"#,
)?;

let chals = parse_all().unwrap();

assert_eq!(
chals[0].pods,
vec![PodType::Template(Pod {
name: "foo".to_string(),
image_source: ImageSource::Image("nginx".to_string()),
architecture: "amd64".to_string(),
env: ListOrMap::Map(HashMap::new()),
resources: Some(Resource {
cpu: 4,
memory: "1Gi".to_string()
}),
replicas: 1,
ports: vec![PortConfig {
internal: 80,
expose: ExposeType::Http("test.chals.example.com".to_string())
}],
volume: None
})]
);

Ok(())
})
}
Loading