diff --git a/src/httpserver.rs b/src/httpserver.rs index 39ca674..a4ff0e1 100644 --- a/src/httpserver.rs +++ b/src/httpserver.rs @@ -116,19 +116,42 @@ fn request(snare: &Arc, mut stream: TcpStream) { None => None, }; - if !body.starts_with("payload=".as_bytes()) { - snare.warn("Payload does not start with 'payload='"); - http_400(stream); - return; - } - let json_str = match percent_decode(&body[8..]).decode_utf8() { - Ok(x) => x.to_string(), - Err(_) => { - snare.warn("JSON not valid UTF-8"); + let json_str = match headers.get("content-type").map(|x| x.as_str()) { + Some("application/json") => match std::str::from_utf8(&body) { + Ok(x) => x.to_owned(), + Err(_) => { + snare.warn("JSON not valid UTF-8"); + http_400(stream); + return; + } + }, + Some("application/x-www-form-urlencoded") => { + if !body.starts_with("payload=".as_bytes()) { + snare.warn("Payload does not start with 'payload='"); + http_400(stream); + return; + } + match percent_decode(&body[8..]).decode_utf8() { + Ok(x) => x.to_string(), + Err(_) => { + snare.warn("JSON not valid UTF-8"); + http_400(stream); + return; + } + } + } + Some(x) => { + snare.warn(&format!("HTTP request: Unknown Content-Type '{x}'")); + http_400(stream); + return; + } + None => { + snare.warn("HTTP request: Content-Type header missing"); http_400(stream); return; } }; + let jv = match serde_json::from_str::(&json_str) { Ok(x) => x, Err(e) => { diff --git a/tests/actions.rs b/tests/actions.rs index 6d44b81..b5e67c2 100644 --- a/tests/actions.rs +++ b/tests/actions.rs @@ -49,9 +49,9 @@ github {{ Ok(format!( r#"POST /payload HTTP/1.1 Host: 127.0.0.1:{port} -Content-Length: 104 +Content-Length: 96 X-GitHub-Delivery: 72d3162e-cc78-11e3-81ab-4c9367dc0958 -X-Hub-Signature-256: sha256=292e1ce3568fecd98589c71938e19afee9b04b7fe11886d5478d802416bbde66 +X-Hub-Signature-256: sha256=d11297e14fe5286dd68fd58c5e23ea7fb45e60ceff51ec3eb3729400fcbcb4b2 User-Agent: GitHub-Hookshot/044aadd Content-Type: application/json X-GitHub-Event: issues @@ -59,7 +59,7 @@ X-GitHub-Hook-ID: 292430182 X-GitHub-Hook-Installation-Target-ID: 79929171 X-GitHub-Hook-Installation-Target-Type: repository -payload={{ +{{ "repository": {{ "owner": {{ "login": "testuser" @@ -115,9 +115,9 @@ github {{ Ok(format!( r#"POST /payload HTTP/1.1 Host: 127.0.0.1:{port} -Content-Length: 104 +Content-Length: 96 X-GitHub-Delivery: 72d3162e-cc78-11e3-81ab-4c9367dc0958 -X-Hub-Signature-256: sha256=292e1ce3568fecd98589c71938e19afee9b04b7fe11886d5478d802416bbde66 +X-Hub-Signature-256: sha256=d11297e14fe5286dd68fd58c5e23ea7fb45e60ceff51ec3eb3729400fcbcb4b2 User-Agent: GitHub-Hookshot/044aadd Content-Type: application/json X-GitHub-Event: issues @@ -125,7 +125,7 @@ X-GitHub-Hook-ID: 292430182 X-GitHub-Hook-Installation-Target-ID: 79929171 X-GitHub-Hook-Installation-Target-Type: repository -payload={{ +{{ "repository": {{ "owner": {{ "login": "testuser" diff --git a/tests/auth.rs b/tests/auth.rs index e5072ea..1e9f138 100644 --- a/tests/auth.rs +++ b/tests/auth.rs @@ -33,15 +33,15 @@ github {{ fn req(port: u16, good_sha256: bool, event_type: &str) -> String { let sha256 = if good_sha256 { - "292e1ce3568fecd98589c71938e19afee9b04b7fe11886d5478d802416bbde66" + "d11297e14fe5286dd68fd58c5e23ea7fb45e60ceff51ec3eb3729400fcbcb4b2" } else { - "292e1ce3568fecd98589c71938e19afee9b04b7fe11886d5478d802416bbde67" + "d11297e14fe5286dd68fd58c5e23ea7fb45e60ceff51ec3eb3729400fcbcb4b3" }; format!( r#"POST /payload HTTP/1.1 Host: 127.0.0.1:{port} -Content-Length: 104 +Content-Length: 96 X-GitHub-Delivery: 72d3162e-cc78-11e3-81ab-4c9367dc0958 X-Hub-Signature-256: sha256={sha256} User-Agent: GitHub-Hookshot/044aadd @@ -51,7 +51,7 @@ X-GitHub-Hook-ID: 292430182 X-GitHub-Hook-Installation-Target-ID: 79929171 X-GitHub-Hook-Installation-Target-Type: repository -payload={{ +{{ "repository": {{ "owner": {{ "login": "testuser" diff --git a/tests/basic.rs b/tests/basic.rs new file mode 100644 index 0000000..7706eaa --- /dev/null +++ b/tests/basic.rs @@ -0,0 +1,100 @@ +use std::error::Error; + +mod common; +use common::run_success; + +#[test] +fn content_type_json() -> Result<(), Box> { + run_success( + r#" + listen = "127.0.0.1:0"; + github { + match ".*" { + cmd = "true"; + secret = "secretsecret"; + } + } + "#, + &[( + move |port| { + Ok(format!( + r#"POST /payload HTTP/1.1 +Host: 127.0.0.1:{port} +Content-Length: 96 +X-GitHub-Delivery: 72d3162e-cc78-11e3-81ab-4c9367dc0958 +X-Hub-Signature-256: sha256=d11297e14fe5286dd68fd58c5e23ea7fb45e60ceff51ec3eb3729400fcbcb4b2 +User-Agent: GitHub-Hookshot/044aadd +Content-Type: application/json +X-GitHub-Event: issues +X-GitHub-Hook-ID: 292430182 +X-GitHub-Hook-Installation-Target-ID: 79929171 +X-GitHub-Hook-Installation-Target-Type: repository + +{{ + "repository": {{ + "owner": {{ + "login": "testuser" + }}, + "name": "testrepo" + }} +}}"# + )) + }, + move |response: String| { + if response.starts_with("HTTP/1.1 200 OK") { + Ok(()) + } else { + Err(format!("Received HTTP response '{response}'").into()) + } + }, + )], + ) +} + +#[test] +fn content_type_url_encoded() -> Result<(), Box> { + run_success( + r#" + listen = "127.0.0.1:0"; + github { + match ".*" { + cmd = "true"; + secret = "secretsecret"; + } + } + "#, + &[( + move |port| { + Ok(format!( + r#"POST /payload HTTP/1.1 +Host: 127.0.0.1:{port} +Content-Length: 104 +X-GitHub-Delivery: 72d3162e-cc78-11e3-81ab-4c9367dc0958 +X-Hub-Signature-256: sha256=292e1ce3568fecd98589c71938e19afee9b04b7fe11886d5478d802416bbde66 +User-Agent: GitHub-Hookshot/044aadd +Content-Type: application/x-www-form-urlencoded +X-GitHub-Event: issues +X-GitHub-Hook-ID: 292430182 +X-GitHub-Hook-Installation-Target-ID: 79929171 +X-GitHub-Hook-Installation-Target-Type: repository + +payload={{ + "repository": {{ + "owner": {{ + "login": "testuser" + }}, + "name": "testrepo" + }} +}}"# + )) + }, + move |response: String| { + if response.starts_with("HTTP/1.1 200 OK") { + Ok(()) + } else { + Err(format!("Received HTTP response '{response}'").into()) + } + }, + )], + ) +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index a614780..43cf222 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -158,7 +158,7 @@ fn snare_command(cfg: &str) -> Result<(Child, NamedTempFile), Box> { let tp = Builder::new().tempfile_in(env!("CARGO_TARGET_TMPDIR"))?; cmd.env("SNARE_DEBUG_PORT_PATH", tp.path().to_str().unwrap()); cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); - cmd.args(["-d", "-c", tc.path().to_str().unwrap()]); + cmd.args(["-d", "-v", "-c", tc.path().to_str().unwrap()]); let sn = cmd.spawn()?; // We want to wait for snare to fully initialise: there is no way of doing that other than // waiting and hoping. diff --git a/tests/queue.rs b/tests/queue.rs index 06700a0..27c8059 100644 --- a/tests/queue.rs +++ b/tests/queue.rs @@ -23,9 +23,9 @@ fn run_queue( Ok(format!( r#"POST /payload HTTP/1.1 Host: 127.0.0.1:{port} -Content-Length: 104 +Content-Length: 96 X-GitHub-Delivery: 72d3162e-cc78-11e3-81ab-4c9367dc0958 -X-Hub-Signature-256: sha256=292e1ce3568fecd98589c71938e19afee9b04b7fe11886d5478d802416bbde66 +X-Hub-Signature-256: sha256=d11297e14fe5286dd68fd58c5e23ea7fb45e60ceff51ec3eb3729400fcbcb4b2 User-Agent: GitHub-Hookshot/044aadd Content-Type: application/json X-GitHub-Event: issues @@ -33,7 +33,7 @@ X-GitHub-Hook-ID: 292430182 X-GitHub-Hook-Installation-Target-ID: 79929171 X-GitHub-Hook-Installation-Target-Type: repository -payload={{ +{{ "repository": {{ "owner": {{ "login": "testuser"