-
Notifications
You must be signed in to change notification settings - Fork 61
feat: add AWF_ONE_SHOT_TOKEN_DEBUG env var for silent-by-default logging #864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -8,6 +8,26 @@ This protects against exfiltration via `/proc/self/environ` inspection while all | |||
|
|
||||
| ## Configuration | ||||
|
|
||||
| ### Debug Logging | ||||
|
|
||||
| By default, the library operates **silently** with no output to stderr. To enable debug logging, set the `AWF_ONE_SHOT_TOKEN_DEBUG` environment variable: | ||||
|
|
||||
| ```bash | ||||
| # Enable debug logging | ||||
| export AWF_ONE_SHOT_TOKEN_DEBUG=1 | ||||
| # or | ||||
| export AWF_ONE_SHOT_TOKEN_DEBUG=true | ||||
|
|
||||
| # Run your command with the library preloaded | ||||
| LD_PRELOAD=/usr/local/lib/one-shot-token.so ./your-program | ||||
| ``` | ||||
|
|
||||
| **Important notes:** | ||||
| - Debug logging is **off by default** to reduce noise in production environments | ||||
| - When enabled, the library logs initialization messages and token access events to stderr | ||||
| - The `AWF_ONE_SHOT_TOKEN_DEBUG` variable is never cached or cleared (prevents infinite recursion) | ||||
| - Set to `"1"` or `"true"` (case-insensitive) to enable debug logging | ||||
|
|
||||
| ### Default Protected Tokens | ||||
|
|
||||
| By default, the library protects these token variables: | ||||
|
|
@@ -223,27 +243,32 @@ EOF | |||
| # Compile the test program | ||||
| gcc -o test_getenv test_getenv.c | ||||
|
|
||||
| # Test with the one-shot token library preloaded | ||||
| # Test with the one-shot token library preloaded (with debug logging) | ||||
| export GITHUB_TOKEN="test-token-12345" | ||||
| export AWF_ONE_SHOT_TOKEN_DEBUG=1 | ||||
| LD_PRELOAD=./one-shot-token.so ./test_getenv | ||||
| ``` | ||||
|
|
||||
| Expected output: | ||||
| Expected output (with debug logging enabled): | ||||
| ``` | ||||
| [one-shot-token] Initialized with 11 default token(s) | ||||
| [one-shot-token] Token GITHUB_TOKEN accessed and cached (value: test...) | ||||
| [one-shot-token] INFO: Token GITHUB_TOKEN cleared from process environment | ||||
|
||||
| [one-shot-token] INFO: Token GITHUB_TOKEN cleared from process environment |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,9 @@ | |
| * AWF_ONE_SHOT_TOKENS - Comma-separated list of token names to protect | ||
| * If not set, uses built-in defaults | ||
| * | ||
| * AWF_ONE_SHOT_TOKEN_DEBUG - Enable debug logging output (default: off) | ||
| * Set to "1" or "true" to enable logging. Logging is silent by default. | ||
| * | ||
| * Build hardening: | ||
| * Default token names are XOR-obfuscated to prevent cleartext extraction | ||
| * via strings(1) or objdump. Internal symbols use hidden visibility. | ||
|
|
@@ -126,6 +129,9 @@ static __thread int in_getenv = 0; | |
| /* Initialization flag */ | ||
| static int tokens_initialized = 0; | ||
|
|
||
| /* Debug logging flag (controlled by AWF_ONE_SHOT_TOKEN_DEBUG environment variable) */ | ||
| static int debug_enabled = 0; | ||
|
|
||
| /* Pointer to the real getenv function */ | ||
| static char *(*real_getenv)(const char *name) = NULL; | ||
|
|
||
|
|
@@ -149,6 +155,34 @@ static void ensure_real_secure_getenv(void) { | |
| /* secure_getenv may not be available on all systems - that's OK */ | ||
| } | ||
|
|
||
| /** | ||
| * Check if debug logging is enabled via AWF_ONE_SHOT_TOKEN_DEBUG environment variable. | ||
| * Returns 1 if AWF_ONE_SHOT_TOKEN_DEBUG is set to "1" or "true" (case-insensitive), 0 otherwise. | ||
| * | ||
| * CRITICAL: This function must call the real getenv directly to avoid infinite recursion | ||
| * when checking the debug flag during initialization. The AWF_ONE_SHOT_TOKEN_DEBUG variable | ||
| * is never cached or cleared by this library. | ||
| */ | ||
| static int is_debug_enabled(void) { | ||
| const char *debug_value = real_getenv("AWF_ONE_SHOT_TOKEN_DEBUG"); | ||
|
|
||
| if (debug_value == NULL || debug_value[0] == '\0') { | ||
| return 0; | ||
| } | ||
|
|
||
| /* Check if value is "1" */ | ||
| if (strcmp(debug_value, "1") == 0) { | ||
| return 1; | ||
| } | ||
|
|
||
| /* Check if value is "true" (case-insensitive) */ | ||
| if (strcasecmp(debug_value, "true") == 0) { | ||
| return 1; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| /** | ||
| * Initialize the token list from AWF_ONE_SHOT_TOKENS environment variable | ||
| * or use defaults if not set. This is called once at first getenv() call. | ||
|
|
@@ -159,6 +193,9 @@ static void init_token_list(void) { | |
| return; | ||
| } | ||
|
|
||
| /* Check if debug logging is enabled */ | ||
| debug_enabled = is_debug_enabled(); | ||
|
|
||
| /* Get the configuration from environment */ | ||
| const char *config = real_getenv("AWF_ONE_SHOT_TOKENS"); | ||
|
Comment on lines
+196
to
200
|
||
|
|
||
|
|
@@ -208,12 +245,16 @@ static void init_token_list(void) { | |
| /* If AWF_ONE_SHOT_TOKENS was set but resulted in zero tokens (e.g., ",,," or whitespace only), | ||
| * fall back to defaults to avoid silently disabling all protection */ | ||
| if (num_tokens == 0) { | ||
| fprintf(stderr, "[one-shot-token] WARNING: AWF_ONE_SHOT_TOKENS was set but parsed to zero tokens\n"); | ||
| fprintf(stderr, "[one-shot-token] WARNING: Falling back to default token list to maintain protection\n"); | ||
| if (debug_enabled) { | ||
| fprintf(stderr, "[one-shot-token] WARNING: AWF_ONE_SHOT_TOKENS was set but parsed to zero tokens\n"); | ||
| fprintf(stderr, "[one-shot-token] WARNING: Falling back to default token list to maintain protection\n"); | ||
| } | ||
| /* num_tokens is already 0 here; assignment is defensive programming for future refactoring */ | ||
| num_tokens = 0; | ||
| } else { | ||
| fprintf(stderr, "[one-shot-token] Initialized with %d custom token(s) from AWF_ONE_SHOT_TOKENS\n", num_tokens); | ||
| if (debug_enabled) { | ||
| fprintf(stderr, "[one-shot-token] Initialized with %d custom token(s) from AWF_ONE_SHOT_TOKENS\n", num_tokens); | ||
| } | ||
| tokens_initialized = 1; | ||
| return; | ||
| } | ||
|
|
@@ -234,7 +275,9 @@ static void init_token_list(void) { | |
| num_tokens++; | ||
| } | ||
|
|
||
| fprintf(stderr, "[one-shot-token] Initialized with %d default token(s)\n", num_tokens); | ||
| if (debug_enabled) { | ||
| fprintf(stderr, "[one-shot-token] Initialized with %d default token(s)\n", num_tokens); | ||
| } | ||
|
|
||
| tokens_initialized = 1; | ||
| } | ||
|
|
@@ -348,8 +391,10 @@ char *getenv(const char *name) { | |
| /* Unset the variable from the environment so /proc/self/environ is cleared */ | ||
| unsetenv(name); | ||
|
|
||
| fprintf(stderr, "[one-shot-token] Token %s accessed and cached (value: %s)\n", | ||
| name, format_token_value(token_cache[token_idx])); | ||
| if (debug_enabled) { | ||
| fprintf(stderr, "[one-shot-token] Token %s accessed and cached (value: %s)\n", | ||
| name, format_token_value(token_cache[token_idx])); | ||
| } | ||
|
|
||
| result = token_cache[token_idx]; | ||
| } | ||
|
|
@@ -412,8 +457,10 @@ char *secure_getenv(const char *name) { | |
| /* Unset the variable from the environment so /proc/self/environ is cleared */ | ||
| unsetenv(name); | ||
|
|
||
| fprintf(stderr, "[one-shot-token] Token %s accessed and cached (value: %s) (via secure_getenv)\n", | ||
| name, format_token_value(token_cache[token_idx])); | ||
| if (debug_enabled) { | ||
| fprintf(stderr, "[one-shot-token] Token %s accessed and cached (value: %s) (via secure_getenv)\n", | ||
| name, format_token_value(token_cache[token_idx])); | ||
| } | ||
|
Comment on lines
459
to
+463
|
||
|
|
||
| result = token_cache[token_idx]; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bullet says
AWF_ONE_SHOT_TOKEN_DEBUGis “never cached or cleared”, but neither the C nor Rust implementation currently prevents a user from including it inAWF_ONE_SHOT_TOKENS(in which case it would be treated like a protected token). Either enforce the exclusion in code or soften this statement to reflect the actual behavior.