71 lines
3.3 KiB
C++
71 lines
3.3 KiB
C++
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||
|
|
|
||
|
|
module Crafter.Review:Tools_impl;
|
||
|
|
import :Tools;
|
||
|
|
import std;
|
||
|
|
|
||
|
|
namespace Crafter::Review::Tools {
|
||
|
|
std::vector<std::string> McpToolNames() {
|
||
|
|
std::vector<std::string> out;
|
||
|
|
for (const std::string_view tool : McpTools) out.push_back(std::format("mcp__{}__{}", McpServer, tool));
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::vector<std::string> McpDeniedToolNames() {
|
||
|
|
std::vector<std::string> out;
|
||
|
|
for (const std::string_view tool : McpDenied) out.push_back(std::format("mcp__{}__{}", McpServer, tool));
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
const std::vector<std::string>& Deny() {
|
||
|
|
static const std::vector<std::string> deny = [] {
|
||
|
|
std::vector<std::string> out = {
|
||
|
|
"Edit", "Write", "MultiEdit", "NotebookEdit", "WebFetch", "WebSearch", "Task", "Agent", "TodoWrite",
|
||
|
|
"Bash(ssh:*)", "Bash(adb:*)", "Bash(rm:*)", "Bash(mv:*)", "Bash(cp:*)", "Bash(git push:*)", "Bash(git commit:*)",
|
||
|
|
"Bash(git checkout:*)", "Bash(git reset:*)", "Bash(git am:*)", "Bash(make:*)", "Bash(npm:*)", "Bash(npx:*)", "Bash(node:*)",
|
||
|
|
"Bash(supabase:*)", "Bash(docker:*)", "Bash(podman:*)", "Bash(psql:*)",
|
||
|
|
};
|
||
|
|
std::ranges::move(McpDeniedToolNames(), std::back_inserter(out));
|
||
|
|
return out;
|
||
|
|
}();
|
||
|
|
return deny;
|
||
|
|
}
|
||
|
|
|
||
|
|
const std::vector<std::string>& BaseAllow() {
|
||
|
|
static const std::vector<std::string> allow = [] {
|
||
|
|
std::vector<std::string> out = {
|
||
|
|
"Read", "Glob", "Grep", "Bash(cat:*)", "Bash(rg:*)", "Bash(grep:*)", "Bash(wc:*)", "Bash(head:*)", "Bash(tail:*)", "Bash(sed -n:*)",
|
||
|
|
"Bash(ls:*)", "Bash(find:*)", "Bash(diff:*)", "Bash(sha256sum:*)", "Bash(sort:*)", "Bash(uniq:*)", "Bash(cut:*)", "Bash(awk:*)",
|
||
|
|
"Bash(echo:*)", "Bash(printf:*)", "Bash(cd:*)", "Bash(python3 -:*)",
|
||
|
|
};
|
||
|
|
for (const std::string_view verb : GitReadOnly) out.push_back(std::format("Bash(git {}:*)", verb));
|
||
|
|
return out;
|
||
|
|
}();
|
||
|
|
return allow;
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
// re.match(r'^Bash\((.+):\*\)$', tool).group(1): the prefix of a
|
||
|
|
// Bash(<prefix>:*) entry. `.` stops at a newline and `$` also matches
|
||
|
|
// before a final one, so a prefix never holds a newline.
|
||
|
|
std::optional<std::string_view> BashPrefix(std::string_view tool) {
|
||
|
|
std::string_view body = tool;
|
||
|
|
if (body.ends_with('\n')) body.remove_suffix(1);
|
||
|
|
if (body.size() < 9 || !body.starts_with("Bash(") || !body.ends_with(":*)")) return std::nullopt;
|
||
|
|
const std::string_view prefix = body.substr(5, body.size() - 8);
|
||
|
|
if (prefix.contains('\n')) return std::nullopt;
|
||
|
|
return prefix;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Denied(std::string_view tool) {
|
||
|
|
if (std::ranges::contains(Deny(), tool)) return true;
|
||
|
|
const std::optional<std::string_view> prefix = BashPrefix(tool);
|
||
|
|
if (!prefix) return false;
|
||
|
|
return std::ranges::any_of(Deny(), [&](const std::string& entry) {
|
||
|
|
const std::optional<std::string_view> denied = BashPrefix(entry);
|
||
|
|
return denied && (*prefix == *denied || prefix->starts_with(std::format("{} ", *denied)));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|