mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
fix(install): declare a transport in generated Codex agent role TOMLs
Codex deserializes ~/.codex/agents/*.toml standalone: an [mcp_servers.codebase-memory-mcp] table with only enabled_tools fails with "invalid transport" and the whole role is dropped, so all three generated roles have been dead on Codex since they first shipped in v0.9.1-rc.1. Render command = <installed binary> plus args = ["--tool-profile=scout|analysis"] in direct Codex profiles, mirroring the Kiro renderer, and keep enabled_tools. Recognize the rc.1 transport-less rendering as a released shape so install migrates those files in place and uninstall still removes them; pass the installed binary path in the Codex uninstall set so exact-content ownership keeps matching. Fixes #1391 Signed-off-by: Ivan Dergachev <dergachoff@gmail.com>
This commit is contained in:
1 parent
d6be58ef9d
commit
07885ef6a9
5 files changed
+166
-40
No files matched your search
+65
-17
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
#include "cli/agent_profiles.h"
|
||||
|
||||
#include "cli/config_toml_edit.h"
|
||||
#include "yyjson/yyjson.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
@@ -451,9 +452,51 @@ static char *render_kiro_profile(cbm_graph_tier_t tier, cbm_graph_access_t acces
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Codex deserializes role files standalone: a server table without command/url
|
||||
* is rejected as "invalid transport" and the whole role is dropped, so direct
|
||||
* profiles must declare the transport. rc1_transportless reproduces the
|
||||
* v0.9.1-rc.1 rendering so installs can migrate those files. */
|
||||
static bool append_codex_profile(profile_buffer_t *buffer, cbm_graph_tier_t tier,
|
||||
cbm_graph_access_t access, const char *binary_path,
|
||||
const char *prompt, bool rc1_transportless) {
|
||||
if (!profile_buffer_append(buffer, "name = \"") ||
|
||||
!profile_buffer_append(buffer, cbm_graph_tier_slug(tier)) ||
|
||||
!profile_buffer_append(buffer, "\"\ndescription = \"") ||
|
||||
!profile_buffer_append(buffer, profile_description(tier, access)) ||
|
||||
!profile_buffer_append(buffer, "\"\nsandbox_mode = \"read-only\"\ndeveloper_instructions = "
|
||||
"\"\"\"\n") ||
|
||||
!profile_buffer_append(buffer, prompt) || !profile_buffer_append(buffer, "\"\"\"\n")) {
|
||||
return false;
|
||||
}
|
||||
if (access != CBM_GRAPH_ACCESS_DIRECT) {
|
||||
return true;
|
||||
}
|
||||
if (!profile_buffer_append(buffer, "\n[mcp_servers.codebase-memory-mcp]\n")) {
|
||||
return false;
|
||||
}
|
||||
if (!rc1_transportless) {
|
||||
char escaped_binary[8192];
|
||||
if (!binary_path || !binary_path[0] ||
|
||||
cbm_toml_escape_basic_string(binary_path, escaped_binary, sizeof(escaped_binary)) !=
|
||||
0) {
|
||||
return false;
|
||||
}
|
||||
if (!profile_buffer_append(buffer, "command = \"") ||
|
||||
!profile_buffer_append(buffer, escaped_binary) ||
|
||||
!profile_buffer_append(buffer, "\"\nargs = [\"--tool-profile=") ||
|
||||
!profile_buffer_append(buffer, tier_server_profile(tier)) ||
|
||||
!profile_buffer_append(buffer, "\"]\n")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return profile_buffer_append(buffer, "enabled_tools = [") &&
|
||||
append_toml_mcp_tools(buffer, CBM_GRAPH_DIALECT_CODEX, tier, false) &&
|
||||
profile_buffer_append(buffer, "]\n");
|
||||
}
|
||||
|
||||
static bool render_profile_text(profile_buffer_t *buffer, cbm_graph_profile_dialect_t dialect,
|
||||
cbm_graph_tier_t tier, cbm_graph_access_t access,
|
||||
const char *prompt) {
|
||||
const char *binary_path, const char *prompt) {
|
||||
const char *slug = cbm_graph_tier_slug(tier);
|
||||
const char *display = cbm_graph_tier_display_name(tier);
|
||||
const char *description = profile_description(tier, access);
|
||||
@@ -471,21 +514,7 @@ static bool render_profile_text(profile_buffer_t *buffer, cbm_graph_profile_dial
|
||||
}
|
||||
return true;
|
||||
case CBM_GRAPH_DIALECT_CODEX:
|
||||
if (!profile_buffer_append(buffer, "name = \"") || !profile_buffer_append(buffer, slug) ||
|
||||
!profile_buffer_append(buffer, "\"\ndescription = \"") ||
|
||||
!profile_buffer_append(buffer, description) ||
|
||||
!profile_buffer_append(
|
||||
buffer, "\"\nsandbox_mode = \"read-only\"\ndeveloper_instructions = \"\"\"\n") ||
|
||||
!profile_buffer_append(buffer, prompt) || !profile_buffer_append(buffer, "\"\"\"\n")) {
|
||||
return false;
|
||||
}
|
||||
if (direct && (!profile_buffer_append(
|
||||
buffer, "\n[mcp_servers.codebase-memory-mcp]\nenabled_tools = [") ||
|
||||
!append_toml_mcp_tools(buffer, dialect, tier, false) ||
|
||||
!profile_buffer_append(buffer, "]\n"))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return append_codex_profile(buffer, tier, access, binary_path, prompt, false);
|
||||
case CBM_GRAPH_DIALECT_GEMINI:
|
||||
if (!append_yaml_identity(buffer, slug, description) ||
|
||||
!profile_buffer_append(buffer,
|
||||
@@ -627,7 +656,26 @@ char *cbm_render_graph_profile(cbm_graph_profile_dialect_t dialect, cbm_graph_ti
|
||||
}
|
||||
profile_buffer_t buffer;
|
||||
profile_buffer_init(&buffer);
|
||||
bool ok = render_profile_text(&buffer, dialect, tier, access, prompt);
|
||||
bool ok = render_profile_text(&buffer, dialect, tier, access, binary_path, prompt);
|
||||
free(prompt);
|
||||
if (!ok) {
|
||||
profile_buffer_discard(&buffer);
|
||||
return NULL;
|
||||
}
|
||||
return profile_buffer_finish(&buffer);
|
||||
}
|
||||
|
||||
char *cbm_render_graph_profile_codex_rc1(cbm_graph_tier_t tier) {
|
||||
if (!tier_valid(tier)) {
|
||||
return NULL;
|
||||
}
|
||||
char *prompt = cbm_render_graph_prompt(tier, CBM_GRAPH_ACCESS_DIRECT);
|
||||
if (!prompt) {
|
||||
return NULL;
|
||||
}
|
||||
profile_buffer_t buffer;
|
||||
profile_buffer_init(&buffer);
|
||||
bool ok = append_codex_profile(&buffer, tier, CBM_GRAPH_ACCESS_DIRECT, NULL, prompt, true);
|
||||
free(prompt);
|
||||
if (!ok) {
|
||||
profile_buffer_discard(&buffer);
|
||||
|
||||
@@ -50,10 +50,14 @@ const char *cbm_graph_tier_display_name(cbm_graph_tier_t tier);
|
||||
bool cbm_graph_dialect_direct_capable(cbm_graph_profile_dialect_t dialect);
|
||||
|
||||
/* Returns malloc-owned profile content, or NULL for invalid/unsafe combinations.
|
||||
* binary_path is required for a direct Kiro profile and ignored otherwise. */
|
||||
* binary_path is required for direct Kiro and Codex profiles and ignored otherwise. */
|
||||
char *cbm_render_graph_profile(cbm_graph_profile_dialect_t dialect, cbm_graph_tier_t tier,
|
||||
cbm_graph_access_t access, const char *binary_path);
|
||||
|
||||
/* v0.9.1-rc.1 direct Codex rendering (server table without a transport), kept
|
||||
* so install/uninstall can recognize and migrate those files. */
|
||||
char *cbm_render_graph_profile_codex_rc1(cbm_graph_tier_t tier);
|
||||
|
||||
/* Vibe stores the behavioral prompt separately from its TOML agent definition.
|
||||
* Other integrations may also use this as the canonical contract text. */
|
||||
char *cbm_render_graph_prompt(cbm_graph_tier_t tier, cbm_graph_access_t access);
|
||||
|
||||
+21
-4
@@ -7390,17 +7390,25 @@ static void install_tiered_agent_profiles(cbm_tiered_profile_set_t profiles, boo
|
||||
access == CBM_GRAPH_ACCESS_DIRECT ? CBM_GRAPH_ACCESS_HANDOFF : CBM_GRAPH_ACCESS_DIRECT;
|
||||
char *alternate = cbm_render_graph_profile(profiles.dialect, tier, alternate_access,
|
||||
profiles.binary_path);
|
||||
const char *released[2];
|
||||
char *codex_rc1 =
|
||||
profiles.dialect == CBM_GRAPH_DIALECT_CODEX && access == CBM_GRAPH_ACCESS_DIRECT
|
||||
? cbm_render_graph_profile_codex_rc1(tier)
|
||||
: NULL;
|
||||
const char *released[3];
|
||||
size_t released_count = 0U;
|
||||
if (alternate) {
|
||||
released[released_count++] = alternate;
|
||||
}
|
||||
if (codex_rc1) {
|
||||
released[released_count++] = codex_rc1;
|
||||
}
|
||||
if (tier == CBM_GRAPH_TIER_VERIFY && profiles.legacy_verify_content) {
|
||||
released[released_count++] = profiles.legacy_verify_content;
|
||||
}
|
||||
int result = prepare_config_parent(path)
|
||||
? cbm_text_migrate_owned_document(path, current, released, released_count)
|
||||
: CLI_ERR;
|
||||
free(codex_rc1);
|
||||
free(alternate);
|
||||
free(current);
|
||||
if (result != CLI_OK) {
|
||||
@@ -7437,15 +7445,23 @@ static void uninstall_tiered_agent_profiles(cbm_tiered_profile_set_t profiles, b
|
||||
access == CBM_GRAPH_ACCESS_DIRECT ? CBM_GRAPH_ACCESS_HANDOFF : CBM_GRAPH_ACCESS_DIRECT;
|
||||
char *alternate = cbm_render_graph_profile(profiles.dialect, tier, alternate_access,
|
||||
profiles.binary_path);
|
||||
const char *released[2];
|
||||
char *codex_rc1 =
|
||||
profiles.dialect == CBM_GRAPH_DIALECT_CODEX && access == CBM_GRAPH_ACCESS_DIRECT
|
||||
? cbm_render_graph_profile_codex_rc1(tier)
|
||||
: NULL;
|
||||
const char *released[3];
|
||||
size_t released_count = 0U;
|
||||
if (alternate) {
|
||||
released[released_count++] = alternate;
|
||||
}
|
||||
if (codex_rc1) {
|
||||
released[released_count++] = codex_rc1;
|
||||
}
|
||||
if (tier == CBM_GRAPH_TIER_VERIFY && profiles.legacy_verify_content) {
|
||||
released[released_count++] = profiles.legacy_verify_content;
|
||||
}
|
||||
int result = cbm_text_remove_owned_document_any(path, current, released, released_count);
|
||||
free(codex_rc1);
|
||||
free(alternate);
|
||||
free(current);
|
||||
if (result < CLI_OK) {
|
||||
@@ -10050,11 +10066,13 @@ static void uninstall_cli_agents(const cbm_detected_agents_t *agents, const char
|
||||
char ip[CLI_BUF_1K];
|
||||
char skills_dir[CLI_BUF_1K];
|
||||
char ap[CLI_BUF_1K];
|
||||
char installed_binary[CLI_BUF_1K];
|
||||
cbm_codex_config_dir(home, config_dir, sizeof(config_dir));
|
||||
snprintf(cp, sizeof(cp), "%s/config.toml", config_dir);
|
||||
snprintf(ip, sizeof(ip), "%s/AGENTS.md", config_dir);
|
||||
snprintf(skills_dir, sizeof(skills_dir), "%s/skills", config_dir);
|
||||
snprintf(ap, sizeof(ap), "%s/agents/codebase-memory.toml", config_dir);
|
||||
cbm_agent_installed_binary_path(home, installed_binary, sizeof(installed_binary));
|
||||
uninstall_agent_mcp_instr((mcp_uninstall_args_t){"Codex CLI", cp, ip}, dry_run,
|
||||
cbm_remove_codex_mcp_owned);
|
||||
uninstall_agent_skill("Codex CLI", skills_dir, dry_run);
|
||||
@@ -10062,6 +10080,7 @@ static void uninstall_cli_agents(const cbm_detected_agents_t *agents, const char
|
||||
(cbm_tiered_profile_set_t){
|
||||
.label = "Codex CLI",
|
||||
.verify_path = ap,
|
||||
.binary_path = installed_binary,
|
||||
.legacy_verify_content = legacy_codex_verify_agent_content,
|
||||
.dialect = CBM_GRAPH_DIALECT_CODEX,
|
||||
},
|
||||
@@ -10071,10 +10090,8 @@ static void uninstall_cli_agents(const cbm_detected_agents_t *agents, const char
|
||||
record_agent_config_error(true, "Codex CLI", "hook_uninstall", cp);
|
||||
}
|
||||
char hooks_json[CLI_BUF_1K];
|
||||
char installed_binary[CLI_BUF_1K];
|
||||
char hook_command[CLI_BUF_8K];
|
||||
snprintf(hooks_json, sizeof(hooks_json), "%s/hooks.json", config_dir);
|
||||
cbm_agent_installed_binary_path(home, installed_binary, sizeof(installed_binary));
|
||||
if (cbm_file_exists(hooks_json) &&
|
||||
(cbm_build_augment_command(installed_binary, hook_command, sizeof(hook_command)) !=
|
||||
CLI_OK ||
|
||||
|
||||
@@ -78,8 +78,10 @@ TEST(agent_profiles_direct_dialects_are_coverage_aware_and_read_only) {
|
||||
for (size_t i = 0U; i < sizeof(direct_dialects) / sizeof(direct_dialects[0]); i++) {
|
||||
const direct_dialect_expectation_t *expectation = &direct_dialects[i];
|
||||
for (int tier = 0; tier < (int)CBM_GRAPH_TIER_COUNT; tier++) {
|
||||
const char *binary =
|
||||
expectation->dialect == CBM_GRAPH_DIALECT_KIRO ? "/opt/codebase memory/cbm" : NULL;
|
||||
const char *binary = expectation->dialect == CBM_GRAPH_DIALECT_KIRO ||
|
||||
expectation->dialect == CBM_GRAPH_DIALECT_CODEX
|
||||
? "/opt/codebase memory/cbm"
|
||||
: NULL;
|
||||
char *profile = cbm_render_graph_profile(expectation->dialect, (cbm_graph_tier_t)tier,
|
||||
CBM_GRAPH_ACCESS_DIRECT, binary);
|
||||
if (!profile) {
|
||||
@@ -225,6 +227,40 @@ TEST(agent_profiles_kiro_is_valid_json_and_escapes_binary_path) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(agent_profiles_codex_declares_transport_and_escapes_binary_path) {
|
||||
const char *binary = "C:\\cbm bin\\codebase-memory-mcp.exe";
|
||||
char *scout = cbm_render_graph_profile(CBM_GRAPH_DIALECT_CODEX, CBM_GRAPH_TIER_SCOUT,
|
||||
CBM_GRAPH_ACCESS_DIRECT, binary);
|
||||
char *verify = cbm_render_graph_profile(CBM_GRAPH_DIALECT_CODEX, CBM_GRAPH_TIER_VERIFY,
|
||||
CBM_GRAPH_ACCESS_DIRECT, binary);
|
||||
ASSERT_NOT_NULL(scout);
|
||||
ASSERT_NOT_NULL(verify);
|
||||
int valid = strstr(scout, "[mcp_servers.codebase-memory-mcp]\n"
|
||||
"command = \"C:\\\\cbm bin\\\\codebase-memory-mcp.exe\"\n"
|
||||
"args = [\"--tool-profile=scout\"]\n"
|
||||
"enabled_tools = [") != NULL &&
|
||||
strstr(verify, "command = \"C:\\\\cbm bin\\\\codebase-memory-mcp.exe\"\n"
|
||||
"args = [\"--tool-profile=analysis\"]\n") != NULL;
|
||||
free(scout);
|
||||
free(verify);
|
||||
ASSERT_TRUE(valid);
|
||||
ASSERT_NULL(cbm_render_graph_profile(CBM_GRAPH_DIALECT_CODEX, CBM_GRAPH_TIER_VERIFY,
|
||||
CBM_GRAPH_ACCESS_DIRECT, NULL));
|
||||
ASSERT_NULL(cbm_render_graph_profile(CBM_GRAPH_DIALECT_CODEX, CBM_GRAPH_TIER_VERIFY,
|
||||
CBM_GRAPH_ACCESS_DIRECT, ""));
|
||||
char *handoff = cbm_render_graph_profile(CBM_GRAPH_DIALECT_CODEX, CBM_GRAPH_TIER_VERIFY,
|
||||
CBM_GRAPH_ACCESS_HANDOFF, NULL);
|
||||
ASSERT_NOT_NULL(handoff);
|
||||
ASSERT_TRUE(strstr(handoff, "[mcp_servers.") == NULL);
|
||||
free(handoff);
|
||||
char *rc1 = cbm_render_graph_profile_codex_rc1(CBM_GRAPH_TIER_VERIFY);
|
||||
ASSERT_NOT_NULL(rc1);
|
||||
ASSERT_TRUE(strstr(rc1, "[mcp_servers.codebase-memory-mcp]\nenabled_tools = [") != NULL);
|
||||
ASSERT_TRUE(strstr(rc1, "command = ") == NULL);
|
||||
free(rc1);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(agent_profiles_vibe_uses_matching_prompt_identifier_and_contract) {
|
||||
for (int tier = 0; tier < (int)CBM_GRAPH_TIER_COUNT; tier++) {
|
||||
const char *slug = cbm_graph_tier_slug((cbm_graph_tier_t)tier);
|
||||
@@ -274,6 +310,7 @@ SUITE(agent_profiles) {
|
||||
RUN_TEST(agent_profiles_handoff_only_dialects_fail_closed_for_direct_access);
|
||||
RUN_TEST(agent_profiles_server_level_dialects_hard_enforce_read_only_tools);
|
||||
RUN_TEST(agent_profiles_kiro_is_valid_json_and_escapes_binary_path);
|
||||
RUN_TEST(agent_profiles_codex_declares_transport_and_escapes_binary_path);
|
||||
RUN_TEST(agent_profiles_vibe_uses_matching_prompt_identifier_and_contract);
|
||||
RUN_TEST(agent_profiles_render_deterministically_and_reject_invalid_inputs);
|
||||
}
|
||||
+36
-16
@@ -13,6 +13,7 @@
|
||||
#include "../src/foundation/compat_thread.h"
|
||||
#include "test_framework.h"
|
||||
#include "test_helpers.h"
|
||||
#include <cli/agent_profiles.h>
|
||||
#include <cli/cli.h>
|
||||
#include <cli/progress_sink.h>
|
||||
#include <daemon/bootstrap.h>
|
||||
@@ -4894,11 +4895,15 @@ TEST(cli_durable_profiles_follow_current_vendor_paths) {
|
||||
files_ok = files_ok && test_file_contains_all(path, claude_terms, 7U);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/agents/codebase-memory.toml", codex_home);
|
||||
const char *const codex_terms[] = {
|
||||
"name = \"codebase-memory\"", "description = ",
|
||||
"developer_instructions = ", "sandbox_mode = \"read-only\"",
|
||||
"[mcp_servers.codebase-memory-mcp]", "check_index_coverage"};
|
||||
files_ok = files_ok && test_file_contains_all(path, codex_terms, 6U);
|
||||
const char *const codex_terms[] = {"name = \"codebase-memory\"",
|
||||
"description = ",
|
||||
"developer_instructions = ",
|
||||
"sandbox_mode = \"read-only\"",
|
||||
"[mcp_servers.codebase-memory-mcp]",
|
||||
"command = \"/opt/codebase-memory-mcp\"",
|
||||
"args = [\"--tool-profile=analysis\"]",
|
||||
"check_index_coverage"};
|
||||
files_ok = files_ok && test_file_contains_all(path, codex_terms, 8U);
|
||||
char *profile = read_test_file_alloc(path);
|
||||
files_ok = files_ok && profile && !strstr(profile, "model =") &&
|
||||
!strstr(profile, "index_repository") && !strstr(profile, "delete_project") &&
|
||||
@@ -5371,25 +5376,40 @@ TEST(cli_tiered_codex_profiles_migrate_preserve_and_uninstall) {
|
||||
"name = \"codebase-memory-scout\"\nuser_note = \"preserve scout\"\n";
|
||||
write_test_file(verify_path, legacy_verify);
|
||||
write_test_file(scout_path, foreign_scout);
|
||||
char *rc1_auditor = cbm_render_graph_profile_codex_rc1(CBM_GRAPH_TIER_AUDIT);
|
||||
if (!rc1_auditor)
|
||||
FAIL("rc.1 auditor rendering must be available");
|
||||
write_test_file(auditor_path, rc1_auditor);
|
||||
free(rc1_auditor);
|
||||
|
||||
char *plan = cbm_build_install_plan_json(tmpdir, "/opt/codebase-memory-mcp");
|
||||
/* Uninstall renders expected content with the installed binary path, so
|
||||
* install with that same path to exercise exact-content removal. */
|
||||
char installed_binary[640];
|
||||
char expected_command[768];
|
||||
snprintf(installed_binary, sizeof(installed_binary), "%s/.local/bin/codebase-memory-mcp",
|
||||
tmpdir);
|
||||
snprintf(expected_command, sizeof(expected_command), "command = \"%s\"", installed_binary);
|
||||
char *plan = cbm_build_install_plan_json(tmpdir, installed_binary);
|
||||
bool plan_ok =
|
||||
plan && strstr(plan, scout_path) && strstr(plan, verify_path) && strstr(plan, auditor_path);
|
||||
free(plan);
|
||||
|
||||
int install_rc = cbm_install_agent_configs(tmpdir, "/opt/codebase-memory-mcp", false, false);
|
||||
int install_rc = cbm_install_agent_configs(tmpdir, installed_binary, false, false);
|
||||
char *scout = read_test_file_alloc(scout_path);
|
||||
char *verify = read_test_file_alloc(verify_path);
|
||||
char *auditor = read_test_file_alloc(auditor_path);
|
||||
bool installed = install_rc != 0 && scout && strcmp(scout, foreign_scout) == 0 && verify &&
|
||||
strcmp(verify, legacy_verify) != 0 && strstr(verify, "Tier 2") &&
|
||||
strstr(verify, "name = \"codebase-memory\"") &&
|
||||
strstr(verify, "check_index_coverage") && auditor &&
|
||||
strstr(auditor, "Tier 3") && strstr(auditor, "check_index_coverage") &&
|
||||
!strstr(verify, "index_repository") && !strstr(verify, "delete_project") &&
|
||||
!strstr(verify, "manage_adr") && !strstr(verify, "ingest_traces") &&
|
||||
!strstr(auditor, "index_repository") && !strstr(auditor, "delete_project") &&
|
||||
!strstr(auditor, "manage_adr") && !strstr(auditor, "ingest_traces");
|
||||
bool installed =
|
||||
install_rc != 0 && scout && strcmp(scout, foreign_scout) == 0 && verify &&
|
||||
strcmp(verify, legacy_verify) != 0 && strstr(verify, "Tier 2") &&
|
||||
strstr(verify, "name = \"codebase-memory\"") && strstr(verify, expected_command) &&
|
||||
strstr(verify, "args = [\"--tool-profile=analysis\"]") &&
|
||||
strstr(verify, "check_index_coverage") && auditor && strstr(auditor, expected_command) &&
|
||||
strstr(auditor, "args = [\"--tool-profile=analysis\"]") && strstr(auditor, "Tier 3") &&
|
||||
strstr(auditor, "check_index_coverage") && !strstr(verify, "index_repository") &&
|
||||
!strstr(verify, "delete_project") && !strstr(verify, "manage_adr") &&
|
||||
!strstr(verify, "ingest_traces") && !strstr(auditor, "index_repository") &&
|
||||
!strstr(auditor, "delete_project") && !strstr(auditor, "manage_adr") &&
|
||||
!strstr(auditor, "ingest_traces");
|
||||
free(scout);
|
||||
free(verify);
|
||||
free(auditor);
|
||||
|
||||
Reference in new issue
Block a user