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
5 changes: 5 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ jobs:

test:
runs-on: ubuntu-latest
env:
CTFLAGS_SEED_FILE_NAME: ctflag.seed
steps:
- name: Checkout source code
uses: actions/checkout@v4

- name: Simulate user registration
run: echo -n segg1545 | tee ${CTFLAGS_SEED_FILE_NAME}

- name: Test the library
run: cargo test

Expand Down
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ctflags"
version = "0.3.6"
version = "0.4.0"
edition = "2024"

[dependencies]
Expand All @@ -11,8 +11,6 @@ tracing = "*"
pyo3 = { version = "*", features = ["extension-module", "abi3-py37"], optional = true }

[lib]
# "lib" est le type par défaut pour l'écosystème Rust.
# "cdylib" est une librairie dynamique de style C, idéale pour être chargée par d'autres langages.
crate-type = ["lib", "staticlib", "cdylib"]

[features]
Expand Down
2 changes: 1 addition & 1 deletion example/testclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def main():
print(f"Flag with salt: {flag_salt}")

# Test with a string context
with_string_context = ctflags.format_flag_from_string_context("segg1545", "example")
with_string_context = ctflags.format_flag_from_context("segg1545", "example")
print(f"Flag with string context is {with_string_context}")
assert with_string_context == "flag(example).5f1b958992ca66c09c0ac9170fce85de"

Expand Down
4 changes: 2 additions & 2 deletions include/ctflags.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extern "C" {
const char* ctflags_get_seed_or_null();
void ctflags_free_string(const char* ptr);
const char* ctflags_format_flag(const char* step, const char* salt);
const char* ctflags_format_flag_from_string_context(const char* context, const char* step, const char* salt);
const char* ctflags_format_flag_from_context(const char* context, const char* step, const char* salt);
}

namespace ctflags {
Expand Down Expand Up @@ -64,7 +64,7 @@ class Flag {
: m_ptr(ctflags_format_flag(step, salt)) {}

Flag(const char* context, const char* step, const char* salt = nullptr)
: m_ptr(ctflags_format_flag_from_string_context(context, step, salt)) {}
: m_ptr(ctflags_format_flag_from_context(context, step, salt)) {}

~Flag() {
ctflags_free_string(m_ptr);
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "maturin"

[project]
name = "ctflags"
version = "0.3.6"
version = "0.4.0"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Rust",
Expand Down
72 changes: 13 additions & 59 deletions src/flags.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::env;
use std::path::PathBuf;

use crate::seed;

Expand All @@ -8,60 +7,36 @@ use crate::seed;
const GLOBAL_SALT_ENV_VAR: &str = "FLAG_GLOBAL_SALT";


pub fn compute_flag_from_context(context: &PathBuf, step: &str, salt: Option<String>) -> String {
pub fn compute_flag_from_context(context: &str, step: &str, salt: Option<String>) -> String {
let mut digest = md5::Context::new();

digest.consume(step);

digest.consume(seed::get_from_context_or_null(context));
if let Ok(global_salt) = env::var(GLOBAL_SALT_ENV_VAR) {
digest.consume(global_salt);
}

if let Some(s) = salt {
digest.consume(s);
}

format!("{:x}", digest.finalize())
}

pub fn compute_flag_from_string_context(context: &str, step: &str, salt: Option<String>) -> String {
let mut digest = md5::Context::new();

digest.consume(step);

digest.consume(context);

if let Some(s) = salt {
digest.consume(s);
}

format!("{:x}", digest.finalize())
}


pub fn compute_flag(step: &str, salt: Option<String>) -> String {
let mut digest = md5::Context::new();

digest.consume(step);

if let Ok(global_salt) = env::var(GLOBAL_SALT_ENV_VAR) {
digest.consume(global_salt);
}

if let Some(app_salt) = salt {
digest.consume(app_salt);
}

digest.consume(seed::get_or_null());

format!("{:x}", digest.finalize())
compute_flag_from_context(&seed::get_or_null(), step, salt)
}


pub fn format_flag(step: &str, salt: Option<String>) -> String {
format!("flag({step}).{}", compute_flag(step, salt))
}

pub fn format_flag_from_string_context(context: &str, step: &str, salt: Option<String>) -> String {
format!("flag({step}).{}", compute_flag_from_string_context(context, step, salt))
pub fn format_flag_from_context(context: &str, step: &str, salt: Option<String>) -> String {
format!("flag({step}).{}", compute_flag_from_context(context, step, salt))
}


Expand All @@ -72,40 +47,19 @@ mod tests {
#[test]
fn legacy_environment_based_flag() {
assert_eq!(compute_flag("example", None),
"1a79a4d60de6718e8e5b326e338ae533");
"5f1b958992ca66c09c0ac9170fce85de");
assert_eq!(compute_flag("example", Some("app noise".to_string())),
"5251b4290fb05756da94df1ec637b5a7");
"c37b2bf9e83b0c886c166bbb7e28c8fe");
}

#[test]
fn same_flag_increasing_noise() {
// Create a context file for this test (to avoid collision with other tests)
let context = seed::create_seed_context(".__example1");
// Save a seed to that context file
assert!(!seed::set_from_context(&context, "segg1545").is_err());

assert_eq!(compute_flag_from_context(&context, "example", None),
"5f1b958992ca66c09c0ac9170fce85de");

assert_eq!(compute_flag_from_context(&context, "example", Some("app noise".to_string())),
"98bf92ea5a1438ed465490c9c2396409");

let _ = seed::clear_from_context(&context);
fn same_flag_with_noise() {
assert_eq!(compute_flag_from_context("segg1545", "example", Some("not a flag".to_string())),
"54ed3a6a399869c751820aee6046668b");
}

#[test]
fn example_flag() {
let context = seed::create_seed_context(".__example2");
assert!(!seed::set_from_context(&context, "segg1545").is_err());

assert_eq!(compute_flag_from_context(&context, "example", None), "5f1b958992ca66c09c0ac9170fce85de");

let _ = seed::clear_from_context(&context);
}

#[test]
// Equivalent to existing example_flag test
fn example_flag_with_string_context() {
assert_eq!(compute_flag_from_string_context("segg1545", "example", None), "5f1b958992ca66c09c0ac9170fce85de");
assert_eq!(compute_flag_from_context("segg1545", "example", None), "5f1b958992ca66c09c0ac9170fce85de");
}
}
6 changes: 3 additions & 3 deletions src/flags_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use std::ffi::{CStr, CString};
use std::os::raw::c_char;

// On importe la fonction Rust originale qu'on veut exposer
use crate::flags::{format_flag, format_flag_from_string_context};
use crate::flags::{format_flag, format_flag_from_context};

#[unsafe(no_mangle)]
pub extern "C" fn ctflags_format_flag_from_string_context(context: *const c_char, step: *const c_char, salt: *const c_char) -> *const c_char {
pub extern "C" fn ctflags_format_flag_from_context(context: *const c_char, step: *const c_char, salt: *const c_char) -> *const c_char {
unsafe {
let context_str = CStr::from_ptr(context).to_str().expect("context contains invalid UTF-8");
let step_str = CStr::from_ptr(step).to_str().expect("step contains invalid UTF-8");
Expand All @@ -17,7 +17,7 @@ pub extern "C" fn ctflags_format_flag_from_string_context(context: *const c_char
Some(salt_str.to_string())
};

let result_string = format_flag_from_string_context(context_str, step_str, salt_option);
let result_string = format_flag_from_context(context_str, step_str, salt_option);

let c_result = CString::new(result_string).unwrap();
c_result.into_raw()
Expand Down
6 changes: 3 additions & 3 deletions src/python_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ fn format_flag(step: &str, salt: Option<String>) -> String {

#[pyfunction]
#[pyo3(signature = (context, step, salt=None))]
fn format_flag_from_string_context(context: &str, step: &str, salt: Option<String>) -> String {
flags::format_flag_from_string_context(context, step, salt)
fn format_flag_from_context(context: &str, step: &str, salt: Option<String>) -> String {
flags::format_flag_from_context(context, step, salt)
}

#[pyfunction]
Expand All @@ -21,7 +21,7 @@ fn get_seed_or_null() -> String {
#[pymodule]
fn ctflags(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(format_flag, m)?)?;
m.add_function(wrap_pyfunction!(format_flag_from_string_context, m)?)?;
m.add_function(wrap_pyfunction!(format_flag_from_context, m)?)?;
m.add_function(wrap_pyfunction!(get_seed_or_null, m)?)?;
Ok(())
}