Skip to content

Commit

Permalink
Secrets and Code scanning alerts API (#730)
Browse files Browse the repository at this point in the history
* Initial structs for Secrets and Code scanning alerts

* Secret scanning alerts functionality

* Params for secret scanning alerts

* Secret alerts test

* Field and test fix

* Correct state in test

* Enums and remove of duplicated struct in Code Scannings
  • Loading branch information
vlad1slav authored Nov 13, 2024
1 parent 6f30dc5 commit 5f6b085
Show file tree
Hide file tree
Showing 11 changed files with 601 additions and 36 deletions.
61 changes: 61 additions & 0 deletions examples/get_code_scanning_alerts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use http::header::ACCEPT;
use octocrab::params::AlertState;
use octocrab::Octocrab;

const OWNER: &str = "org";
const REPO: &str = "some-repo";

#[tokio::main]
async fn main() {
// example for Code Scanning alerts API with OAuth GitHub App
let client_id = secrecy::SecretString::from(std::env::var("GITHUB_CLIENT_ID").unwrap());
let crab = octocrab::Octocrab::builder()
.base_uri("https://github.com")
.unwrap()
.add_header(ACCEPT, "application/json".to_string())
.build()
.unwrap();

let codes = crab
.authenticate_as_device(&client_id, ["security_events"])
.await
.unwrap();
println!(
"Go to {} and enter code {}",
codes.verification_uri, codes.user_code
);
let auth = codes.poll_until_available(&crab, &client_id).await.unwrap();
println!(
"Auth: scope {:?}; token type {}",
auth.scope, auth.token_type
);
let octocrab = Octocrab::builder()
.oauth(auth)
.add_header(ACCEPT, "application/vnd.github+json".to_string())
.build()
.unwrap();
// Get all Code Scanning alerts for a repo
let a = octocrab
.code_scannings(OWNER.to_owned(), REPO.to_owned())
.list()
.send()
.await
.unwrap();
println!("{:?}", a);
// Get a single Code Scanning alert
let single_alert = octocrab
.code_scannings(OWNER.to_owned(), REPO.to_owned())
.get(1)
.await
.unwrap();
println!("{:?}", single_alert);
// Update (Open) a Code Scanning alert
let updated_alert = octocrab
.code_scannings(OWNER.to_owned(), REPO.to_owned())
.update(1)
.state(AlertState::Open)
.send()
.await
.unwrap();
println!("{:?}", updated_alert);
}
69 changes: 69 additions & 0 deletions examples/get_secret_scanning_alerts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use http::header::ACCEPT;
use octocrab::models::repos::secret_scanning_alert::UpdateSecretScanningAlert;
use octocrab::Octocrab;

const OWNER: &str = "org";
const REPO: &str = "some-repo";

#[tokio::main]
async fn main() {
// example for Secret Scanning alerts API with OAuth GitHub App
let client_id = secrecy::SecretString::from(std::env::var("GITHUB_CLIENT_ID").unwrap());
let crab = octocrab::Octocrab::builder()
.base_uri("https://github.com")
.unwrap()
.add_header(ACCEPT, "application/json".to_string())
.build()
.unwrap();

let codes = crab
.authenticate_as_device(&client_id, ["security_events"])
.await
.unwrap();
println!(
"Go to {} and enter code {}",
codes.verification_uri, codes.user_code
);
let auth = codes.poll_until_available(&crab, &client_id).await.unwrap();
println!(
"Auth: scope {:?}; token type {}",
auth.scope, auth.token_type
);
let octocrab = Octocrab::builder()
.oauth(auth)
.add_header(ACCEPT, "application/vnd.github+json".to_string())
.build()
.unwrap();
// Get all Secret Scanning alerts for a repo
let a = octocrab
.repos(OWNER, REPO)
.secrets_scanning()
.direction("asc")
.get_alerts()
.await
.unwrap();
println!("{:?}", a);
// Get a single Secret Scanning alert
let single_alert = octocrab
.repos(OWNER, REPO)
.secrets_scanning()
.get_alert(5)
.await
.unwrap();
println!("{:?}", single_alert);
// Update (dismiss) a Secret Scanning alert
let updated_alert = octocrab
.repos(OWNER, REPO)
.secrets_scanning()
.update_alert(
5,
Some(&UpdateSecretScanningAlert {
state: "resolved",
resolution: Some("used_in_tests"),
resolution_comment: Some("Mock value that is used in tests"),
}),
)
.await
.unwrap();
println!("{:?}", updated_alert);
}
7 changes: 7 additions & 0 deletions src/api/repos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod merges;
mod pulls;
pub mod release_assets;
pub mod releases;
mod secret_scanning_alerts;
mod secrets;
mod stargazers;
mod status;
Expand All @@ -42,6 +43,7 @@ pub use merges::MergeBranchBuilder;
pub use pulls::ListPullsBuilder;
pub use release_assets::ReleaseAssetsHandler;
pub use releases::ReleasesHandler;
pub use secret_scanning_alerts::RepoSecretScanningAlertsHandler;
pub use secrets::RepoSecretsHandler;
pub use stargazers::ListStarGazersBuilder;
pub use status::{CreateStatusBuilder, ListStatusesBuilder};
Expand Down Expand Up @@ -755,6 +757,11 @@ impl<'octo> RepoHandler<'octo> {
RepoDependabotAlertsHandler::new(self)
}

/// Handle secrets scanning alerts on the repository
pub fn secrets_scanning(&self) -> RepoSecretScanningAlertsHandler<'_> {
RepoSecretScanningAlertsHandler::new(self)
}

/// Creates a new Git commit object.
/// See https://docs.github.com/en/rest/git/commits?apiVersion=2022-11-28#create-a-commit
/// ```no_run
Expand Down
207 changes: 207 additions & 0 deletions src/api/repos/secret_scanning_alerts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
use super::RepoHandler;

/// A client to GitHub's repository Secret Scanning API.
///
/// Created with [`Octocrab::repos`].
pub struct RepoSecretScanningAlertsHandler<'octo> {
handler: &'octo RepoHandler<'octo>,
params: Params,
}

#[derive(serde::Serialize)]
struct Params {
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
resolution: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
validity: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
sort: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
direction: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
secret_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
before: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
after: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
is_publicly_leaked: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
is_multi_repo: Option<bool>,
}

impl<'octo> RepoSecretScanningAlertsHandler<'octo> {
pub(crate) fn new(repo: &'octo RepoHandler<'octo>) -> Self {
Self {
handler: repo,
params: Params {
per_page: None,
page: None,
state: None,
after: None,
before: None,
is_multi_repo: None,
is_publicly_leaked: None,
resolution: None,
sort: None,
direction: None,
secret_type: None,
validity: None,
},
}
}

/// Lists all Secret Scanning Alerts available in a repository.
/// You must authenticate using an access token with the `repo` or `security_events` scope to use this endpoint.
/// ```no_run
/// # async fn run() -> octocrab::Result<()> {
/// # let octocrab = octocrab::Octocrab::default();
/// let all_secrets = octocrab.repos("owner", "repo")
/// .secrets_scanning()
/// .direction("asc")
/// .get_alerts()
/// .await?;
/// # Ok(())
/// # }
pub async fn get_alerts(
&self,
) -> crate::Result<crate::Page<crate::models::repos::secret_scanning_alert::SecretScanningAlert>>
{
let route = format!("/{}/secret-scanning/alerts", self.handler.repo);
self.handler.crab.get(route, Some(&self.params)).await
}

/// Results per page (max 100).
pub fn per_page(mut self, per_page: impl Into<u8>) -> Self {
self.params.per_page = Some(per_page.into());
self
}

/// Page number of the results to fetch.
pub fn page(mut self, page: impl Into<u32>) -> Self {
self.params.page = Some(page.into());
self
}

/// Filter Secret Scanning Alerts by state.
pub fn state(mut self, state: impl Into<String>) -> Self {
self.params.state = Some(state.into());
self
}

/// Filter Secret Scanning Alerts by resolution.
pub fn resolution(mut self, resolution: impl Into<Vec<String>>) -> Self {
self.params.resolution = Some(resolution.into());
self
}

/// Filter Secret Scanning Alerts by validity.
pub fn validity(mut self, validity: impl Into<Vec<String>>) -> Self {
self.params.validity = Some(validity.into());
self
}

/// Filter Secret Scanning Alerts by secret_type.
pub fn secret_type(mut self, secret_type: impl Into<String>) -> Self {
self.params.secret_type = Some(secret_type.into());
self
}

/// Filter Secret Scanning Alerts by multi repo alerts.
pub fn is_multi_repo(mut self, is_multi_repo: impl Into<bool>) -> Self {
self.params.is_multi_repo = Some(is_multi_repo.into());
self
}

/// Filter Secret Scanning Alerts by publicly leaked.
pub fn is_publicly_leaked(mut self, is_publicly_leaked: impl Into<bool>) -> Self {
self.params.is_publicly_leaked = Some(is_publicly_leaked.into());
self
}

/// Filter Secret Scanning Alerts by after cursor.
pub fn after(mut self, after: impl Into<String>) -> Self {
self.params.after = Some(after.into());
self
}

/// Filter Secret Scanning Alerts by before cursor.
pub fn before(mut self, before: impl Into<String>) -> Self {
self.params.before = Some(before.into());
self
}

/// Sort Secret Scanning Alerts.
pub fn sort(mut self, sort: impl Into<String>) -> Self {
self.params.sort = Some(sort.into());
self
}

/// Sort direction of Secret Scanning Alerts.
pub fn direction(mut self, direction: impl Into<String>) -> Self {
self.params.direction = Some(direction.into());
self
}

/// Lists single Secret Scanning Alert for a repository.
/// You must authenticate using an access token with the `repo` or `security_events` scope to use this endpoint.
/// ```no_run
/// # async fn run() -> octocrab::Result<()> {
/// # let octocrab = octocrab::Octocrab::default();
/// let all_secrets = octocrab.repos("owner", "repo")
/// .secrets_scanning()
/// .get_alert(5)
/// .await?;
/// # Ok(())
/// # }
pub async fn get_alert(
&self,
alert_number: u32,
) -> crate::Result<crate::models::repos::secret_scanning_alert::SecretScanningAlert> {
let route = format!(
"/{}/secret-scanning/alerts/{}",
self.handler.repo, alert_number
);
self.handler.crab.get(route, None::<&()>).await
}

/// Updates a Secret Scanning alert.
/// You must authenticate using an access token with the `security_events ` scope to use this endpoint.
/// ```no_run
/// # async fn run() -> octocrab::Result<()> {
/// # let octocrab = octocrab::Octocrab::default();
/// use octocrab::models::repos::secret_scanning_alert::UpdateSecretScanningAlert;
///
/// let result = octocrab.repos("owner", "repo")
/// .secrets_scanning()
/// .update_alert(
/// 5,
/// Some(&UpdateSecretScanningAlert {
/// state: "dismissed",
/// resolution: Some("no_bandwidth"),
/// resolution_comment: Some("I don't have time to fix this right now"),
/// })
/// )
/// .await?;
/// # Ok(())
/// # }
pub async fn update_alert(
&self,
alert_number: u32,
alert_update: Option<
&crate::models::repos::secret_scanning_alert::UpdateSecretScanningAlert<'_>,
>,
) -> crate::Result<crate::models::repos::secret_scanning_alert::SecretScanningAlert> {
let route = format!(
"/{}/secret-scanning/alerts/{}",
self.handler.repo, alert_number
);
self.handler.crab.patch(route, alert_update).await
}
}
Loading

0 comments on commit 5f6b085

Please sign in to comment.