fluidattacks-core 0.1.5

Fluid Attacks Core Library
Documentation
use anyhow::{bail, Context, Result};
use regex::Regex;
use uuid::Uuid;

pub struct AwsTempCreds {
    pub access_key: String,
    pub secret_key: String,
    pub session_token: String,
    pub region: String,
}

pub async fn assume_role(arn: &str, external_id: &str, url: &str) -> Result<AwsTempCreds> {
    let region = extract_region(url)?;

    let config = aws_config::defaults(aws_config::BehaviorVersion::latest())
        .region(aws_config::Region::new(region.clone()))
        .load()
        .await;

    let sts = aws_sdk_sts::Client::new(&config);

    let resp = sts
        .assume_role()
        .role_arn(arn)
        .role_session_name(format!("session-{}", Uuid::new_v4()))
        .external_id(external_id)
        .send()
        .await
        .context("STS AssumeRole failed")?;

    let creds = resp
        .credentials()
        .ok_or_else(|| anyhow::anyhow!("no credentials in AssumeRole response"))?;

    let access_key = creds.access_key_id().to_string();
    let secret_key = creds.secret_access_key().to_string();
    let session_token = creds.session_token().to_string();

    if access_key.is_empty() || secret_key.is_empty() {
        bail!("empty credentials from STS AssumeRole");
    }

    Ok(AwsTempCreds {
        access_key,
        secret_key,
        session_token,
        region,
    })
}

fn extract_region(url: &str) -> Result<String> {
    let re = Regex::new(r"codecommit::([a-z0-9-]+)://")?;
    if let Some(caps) = re.captures(url) {
        return Ok(caps[1].to_string());
    }

    // Try to find region in standard CodeCommit URLs
    let re2 = Regex::new(r"git-codecommit\.([a-z0-9-]+)\.amazonaws\.com")?;
    if let Some(caps) = re2.captures(url) {
        return Ok(caps[1].to_string());
    }

    Ok("us-east-1".to_string())
}