Skip to main content

htb_cli/api/
sherlocks.rs

1use serde_json::json;
2
3use crate::error::HtbError;
4use crate::models::sherlock::{Sherlock, SherlockCategoriesResponse, SherlockCategory};
5use crate::models::{ActionResponse, Paginated};
6
7use super::HtbClient;
8
9pub struct SherlockApi<'a>(pub(crate) &'a HtbClient);
10
11impl SherlockApi<'_> {
12    pub async fn list(&self, page: u32, per_page: u32) -> Result<Paginated<Sherlock>, HtbError> {
13        self.0
14            .get(&format!(
15                "/api/v4/sherlocks?per_page={per_page}&page={page}"
16            ))
17            .await
18    }
19
20    pub async fn categories(&self) -> Result<Vec<SherlockCategory>, HtbError> {
21        let resp: SherlockCategoriesResponse =
22            self.0.get("/api/v4/sherlocks/categories/list").await?;
23        Ok(resp.info)
24    }
25
26    pub async fn info(&self, slug: &str) -> Result<Sherlock, HtbError> {
27        let encoded = super::encode_path(slug);
28        self.0.get(&format!("/api/v4/sherlocks/{encoded}")).await
29    }
30
31    pub async fn download_link(&self, sherlock_id: u64) -> Result<String, HtbError> {
32        let resp: crate::models::challenge::ChallengeDownloadResponse = self
33            .0
34            .get(&format!("/api/v4/sherlocks/{sherlock_id}/download_link"))
35            .await?;
36        Ok(resp.url)
37    }
38
39    pub async fn submit_flag(
40        &self,
41        sherlock_id: u64,
42        task_id: u64,
43        flag: &str,
44    ) -> Result<ActionResponse, HtbError> {
45        self.0
46            .post(
47                &format!("/api/v4/sherlocks/{sherlock_id}/tasks/{task_id}/flag"),
48                &json!({"flag": flag}),
49            )
50            .await
51    }
52}