42 lines
2.1 KiB
C++
42 lines
2.1 KiB
C++
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||
|
|
|
||
|
|
// The programs a lens is allowed to run are there: every Bash(<program>
|
||
|
|
// ...:*) entry of the base allowlist (the git verbs included), the shell
|
||
|
|
// claude's Bash tool runs them with, and node and npx for the Playwright MCP
|
||
|
|
// server. The suite runs inside the Flatpak build, the sandbox that ships, so
|
||
|
|
// there every one must resolve on PATH; natively the test names what this
|
||
|
|
// machine lacks and skips.
|
||
|
|
|
||
|
|
#include <unistd.h>
|
||
|
|
import std;
|
||
|
|
import Crafter.Review;
|
||
|
|
namespace fs = std::filesystem;
|
||
|
|
using namespace Crafter::Review;
|
||
|
|
|
||
|
|
int main() {
|
||
|
|
std::println("lens tools: every program a lens's allowlist names is on PATH");
|
||
|
|
std::set<std::string> programs = {"bash", "node", "npx"};
|
||
|
|
for (const std::string& tool : Tools::BaseAllow()) {
|
||
|
|
if (!tool.starts_with("Bash(") || !tool.ends_with(":*)")) continue;
|
||
|
|
const std::string prefix = tool.substr(5, tool.size() - 8);
|
||
|
|
const std::string program = prefix.substr(0, prefix.find(' '));
|
||
|
|
// A shell builtin, never a file of its own.
|
||
|
|
if (program != "cd") programs.insert(program);
|
||
|
|
}
|
||
|
|
const std::vector<std::string> path = Dyn::Split(Platform::GetEnv("PATH").value_or(""), ":");
|
||
|
|
std::vector<std::string> missing;
|
||
|
|
for (const std::string& program : programs) {
|
||
|
|
const bool found = std::ranges::any_of(path, [&](const std::string& dir) { return !dir.empty() && access((fs::path(dir) / program).c_str(), X_OK) == 0; });
|
||
|
|
if (!found) missing.push_back(program);
|
||
|
|
}
|
||
|
|
const bool shipped = fs::exists("/.flatpak-info");
|
||
|
|
if (!missing.empty() && !shipped) {
|
||
|
|
std::println("skipped: not on this machine's PATH: {}", Dyn::Join(missing, ", "));
|
||
|
|
return 77;
|
||
|
|
}
|
||
|
|
const bool ok = missing.empty() && programs.size() >= 20;
|
||
|
|
std::println("{}every program a lens's allowlist names, its shell and the MCP server's node and npx resolve on PATH ({} programs){}", ok ? " ok " : " FAIL ", programs.size(),
|
||
|
|
missing.empty() ? std::string() : std::format(" (missing: {})", Dyn::Join(missing, ", ")));
|
||
|
|
return ok ? 0 : 1;
|
||
|
|
}
|