Skip to content
Open
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
28 changes: 26 additions & 2 deletions .github/scripts/commit_prefix_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@
repo = Repo(".")

# Regex patterns
PREFIX_RE = re.compile(r"^([a-z0-9_]+:)\s+\S", re.IGNORECASE)
#
# Allow test-specific prefixes such as:
# - tests: internal:
# - tests: runtime:
# - tests: integration:
#
# These are normalized back to tests: for path-based validation.
PREFIX_RE = re.compile(
r"^((?:tests:\s+(?:internal|runtime|integration):)|[a-z0-9_]+:)\s+\S",
re.IGNORECASE,
)
Comment on lines +24 to +34
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor inconsistency between regex and normalization logic.

The PREFIX_RE regex uses \s+ which allows multiple whitespace characters between tests: and the subcategory (e.g., tests: internal:). However, normalize_subject_prefix() at lines 60-61 performs exact string matching with single spaces ("tests: internal:").

If a commit uses extra whitespace like tests: internal: add test, the regex will match and extract tests: internal:, but normalization will fall through to line 63 returning the lowered string as-is instead of normalizing to tests:.

Consider normalizing whitespace before comparison:

Proposed fix
 def normalize_subject_prefix(prefix: str) -> str:
     """
     Map accepted subject-prefix variants to the canonical prefix used by
     path-based validation.
     """
-    lowered = prefix.lower()
+    # Normalize internal whitespace to single space before comparison
+    lowered = ' '.join(prefix.lower().split())
 
     if lowered in ("tests: internal:", "tests: runtime:", "tests: integration:"):
         return "tests:"
 
     return lowered

Also applies to: 53-64

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/scripts/commit_prefix_check.py around lines 24 - 34, The regex
PREFIX_RE allows multiple spaces (e.g., "tests:  internal:"), but
normalize_subject_prefix() compares against exact strings like "tests:
internal:" so such inputs won't normalize; update normalize_subject_prefix() to
normalize whitespace before doing comparisons (e.g., collapse consecutive spaces
and trim, or run a regex replacement to convert any run of whitespace between
tokens into a single space) and then perform the existing lower-case and
equality checks so that values matched by PREFIX_RE (and the constant PREFIX_RE
itself) are normalized consistently to "tests:".

SIGNED_OFF_RE = re.compile(r"Signed-off-by:", re.IGNORECASE)
FENCED_BLOCK_RE = re.compile(
r"""
Expand All @@ -40,6 +50,19 @@ def strip_fenced_code_blocks(text: str) -> str:
return FENCED_BLOCK_RE.sub("", text)


def normalize_subject_prefix(prefix: str) -> str:
"""
Map accepted subject-prefix variants to the canonical prefix used by
path-based validation.
"""
lowered = prefix.lower()

if lowered in ("tests: internal:", "tests: runtime:", "tests: integration:"):
return "tests:"

return lowered


# ------------------------------------------------
# Identify expected prefixes dynamically from file paths
# ------------------------------------------------
Expand Down Expand Up @@ -231,6 +254,7 @@ def is_version_bump(commit):
return False, f"Missing prefix in commit subject: '{first_line}'"

subject_prefix = subject_prefix_match.group(1)
normalized_subject_prefix = normalize_subject_prefix(subject_prefix)

# Run squash detection (but ignore multi-signoff errors)
bad_squash, reason = detect_bad_squash(body)
Expand Down Expand Up @@ -281,7 +305,7 @@ def is_version_bump(commit):
return True, ""

expected_lower = {p.lower() for p in expected}
subj_lower = subject_prefix.lower()
subj_lower = normalized_subject_prefix


# ------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions plugins/out_azure_logs_ingestion/azure_logs_ingestion.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,11 @@ static struct flb_config_map config_map[] = {
0, FLB_TRUE, offsetof(struct flb_az_li, client_secret),
"Set the client secret of the AAD application"
},
{
FLB_CONFIG_MAP_STR, "auth_url", (char *)NULL,
0, FLB_TRUE, offsetof(struct flb_az_li, auth_url_override),
"[Optional] Override the OAuth2 token endpoint."
},
{
FLB_CONFIG_MAP_STR, "dce_url", (char *)NULL,
0, FLB_TRUE, offsetof(struct flb_az_li, dce_url),
Expand Down
1 change: 1 addition & 0 deletions plugins/out_azure_logs_ingestion/azure_logs_ingestion.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct flb_az_li {
int compress_enabled;

/* mangement auth */
flb_sds_t auth_url_override;
flb_sds_t auth_url;
struct flb_oauth2 *u_auth;
/* mutex for acquiring tokens */
Expand Down
90 changes: 80 additions & 10 deletions plugins/out_azure_logs_ingestion/azure_logs_ingestion_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,60 @@
#include "azure_logs_ingestion.h"
#include "azure_logs_ingestion_conf.h"

static int validate_auth_url_override(struct flb_output_instance *ins,
flb_sds_t auth_url_override)
{
int ret;
int result;
char *protocol = NULL;
char *host = NULL;
char *port = NULL;
char *uri = NULL;

result = -1;

ret = flb_utils_url_split(auth_url_override, &protocol, &host, &port, &uri);
if (ret == -1 || protocol == NULL || host == NULL) {
flb_plg_error(ins, "property 'auth_url' has an invalid URL");
goto cleanup;
}

if (strcasecmp(protocol, "https") == 0) {
result = 0;
goto cleanup;
}

if (strcasecmp(protocol, "http") != 0) {
flb_plg_error(ins, "property 'auth_url' must use http or https");
goto cleanup;
}

if (strcmp(host, "localhost") == 0 || strcmp(host, "127.0.0.1") == 0) {
result = 0;
goto cleanup;
}

flb_plg_error(ins,
"property 'auth_url' must use https or an explicit loopback "
"http endpoint");

cleanup:
if (protocol != NULL) {
flb_free(protocol);
}
if (host != NULL) {
flb_free(host);
}
if (port != NULL) {
flb_free(port);
}
if (uri != NULL) {
flb_free(uri);
}

return result;
}

struct flb_az_li* flb_az_li_ctx_create(struct flb_output_instance *ins,
struct flb_config *config)
{
Expand Down Expand Up @@ -61,7 +115,7 @@ struct flb_az_li* flb_az_li_ctx_create(struct flb_output_instance *ins,
return NULL;
}
/* config: 'tenant_id' */
if (!ctx->tenant_id) {
if (!ctx->tenant_id && !ctx->auth_url_override) {
flb_plg_error(ins, "property 'tenant_id' is not defined");
flb_az_li_ctx_destroy(ctx);
return NULL;
Expand Down Expand Up @@ -91,16 +145,32 @@ struct flb_az_li* flb_az_li_ctx_create(struct flb_output_instance *ins,
return NULL;
}

/* Allocate and set auth url */
ctx->auth_url = flb_sds_create_size(sizeof(FLB_AZ_LI_AUTH_URL_TMPLT) - 1 +
flb_sds_len(ctx->tenant_id));
if (!ctx->auth_url) {
flb_errno();
flb_az_li_ctx_destroy(ctx);
return NULL;
if (ctx->auth_url_override) {
ret = validate_auth_url_override(ins, ctx->auth_url_override);
if (ret == -1) {
flb_az_li_ctx_destroy(ctx);
return NULL;
}

ctx->auth_url = flb_sds_create(ctx->auth_url_override);
if (!ctx->auth_url) {
flb_errno();
flb_az_li_ctx_destroy(ctx);
return NULL;
}
}
else {
/* Allocate and set auth url */
ctx->auth_url = flb_sds_create_size(sizeof(FLB_AZ_LI_AUTH_URL_TMPLT) - 1 +
flb_sds_len(ctx->tenant_id));
if (!ctx->auth_url) {
flb_errno();
flb_az_li_ctx_destroy(ctx);
return NULL;
}
flb_sds_snprintf(&ctx->auth_url, flb_sds_alloc(ctx->auth_url),
FLB_AZ_LI_AUTH_URL_TMPLT, ctx->tenant_id);
}
flb_sds_snprintf(&ctx->auth_url, flb_sds_alloc(ctx->auth_url),
FLB_AZ_LI_AUTH_URL_TMPLT, ctx->tenant_id);

/* Allocate and set dce full url */
ctx->dce_u_url = flb_sds_create_size(sizeof(FLB_AZ_LI_DCE_URL_TMPLT) - 1 +
Expand Down
1 change: 1 addition & 0 deletions src/flb_oauth2.c
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,7 @@ struct flb_oauth2 *flb_oauth2_create(struct flb_config *config,
(void) expire_sec;

oauth2_apply_defaults(&cfg);
cfg.enabled = FLB_TRUE;
cfg.token_url = flb_sds_create(auth_url);
cfg.refresh_skew = FLB_OAUTH2_DEFAULT_SKEW_SECS;

Expand Down
72 changes: 72 additions & 0 deletions tests/internal/oauth2.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,26 @@ static struct flb_oauth2 *create_oauth_ctx(struct flb_config *config,
return ctx;
}

static struct flb_oauth2 *create_legacy_oauth_ctx(struct flb_config *config,
struct oauth2_mock_server *server)
{
flb_sds_t token_url;
struct flb_oauth2 *ctx;

token_url = flb_sds_create_size(64);
TEST_CHECK(token_url != NULL);
if (!token_url) {
return NULL;
}

flb_sds_printf(&token_url, "http://127.0.0.1:%d/token", server->port);

ctx = flb_oauth2_create(config, token_url, 300);
flb_sds_destroy(token_url);

return ctx;
}

static int write_text_file(const char *path, const char *content)
{
FILE *fp;
Expand Down Expand Up @@ -948,6 +968,57 @@ void test_caching_and_refresh(void)
flb_config_exit(config);
}

void test_legacy_create_manual_payload_flow(void)
{
int ret;
char *token;
struct flb_config *config;
struct flb_oauth2 *ctx;
struct oauth2_mock_server server;

config = flb_config_init();
TEST_CHECK(config != NULL);

ret = oauth2_mock_server_start(&server, 3600, 0);
TEST_CHECK(ret == 0);

ctx = create_legacy_oauth_ctx(config, &server);
TEST_CHECK(ctx != NULL);

#ifdef FLB_SYSTEM_MACOS
ret = oauth2_mock_server_wait_ready(&server);
TEST_CHECK(ret == 0);
#endif

flb_oauth2_payload_clear(ctx);

ret = flb_oauth2_payload_append(ctx, "grant_type", -1,
"client_credentials", -1);
TEST_CHECK(ret == 0);

ret = flb_oauth2_payload_append(ctx, "client_id", -1, "legacy-id", -1);
TEST_CHECK(ret == 0);

ret = flb_oauth2_payload_append(ctx, "client_secret", -1,
"legacy-secret", -1);
TEST_CHECK(ret == 0);

token = flb_oauth2_token_get(ctx);
TEST_CHECK(token != NULL);
TEST_CHECK(server.token_requests == 1);
TEST_CHECK(strcmp(token, "mock-token-1") == 0);
TEST_CHECK(strstr(server.latest_token_request,
"grant_type=client_credentials") != NULL);
TEST_CHECK(strstr(server.latest_token_request,
"client_id=legacy-id") != NULL);
TEST_CHECK(strstr(server.latest_token_request,
"client_secret=legacy-secret") != NULL);

flb_oauth2_destroy(ctx);
oauth2_mock_server_stop(&server);
flb_config_exit(config);
}

void test_private_key_jwt_body(void)
{
int ret;
Expand Down Expand Up @@ -1078,6 +1149,7 @@ TEST_LIST = {
test_parse_rejects_missing_required_fields},
{"parse_rejects_invalid_expires_in", test_parse_rejects_invalid_expires_in},
{"caching_and_refresh", test_caching_and_refresh},
{"legacy_create_manual_payload_flow", test_legacy_create_manual_payload_flow},
{"private_key_jwt_body", test_private_key_jwt_body},
{"private_key_jwt_x5t_header", test_private_key_jwt_x5t_header},
{0}
Expand Down