mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-18 18:27:27 +00:00
fix(scripts): harden clawtributors updater
This commit is contained in:
@@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/Exec approvals: prevent safeBins allowlist bypass via shell expansion (host exec allowlist mode only; not enabled by default). Thanks @christos-eth.
|
||||
- Security/Gateway: block `system.execApprovals.*` via `node.invoke` (use `exec.approvals.node.*` instead). Thanks @christos-eth.
|
||||
- Security/Exec: harden PATH handling by disabling project-local `node_modules/.bin` bootstrapping by default, disallowing node-host `PATH` overrides, and spawning ACP servers via the current executable by default. Thanks @akhmittra.
|
||||
- Scripts: harden clawtributors updater against command injection via untrusted commit metadata. Thanks @scanleale.
|
||||
- CLI: fix lazy core command registration so top-level maintenance commands (`doctor`, `dashboard`, `reset`, `uninstall`) resolve correctly instead of exposing a non-functional `maintenance` placeholder command.
|
||||
- Telegram: when `channels.telegram.commands.native` is `false`, exclude plugin commands from `setMyCommands` menu registration while keeping plugin slash handlers callable. (#15132) Thanks @Glucksberg.
|
||||
- Security/Agents: scope CLI process cleanup to owned child PIDs to avoid killing unrelated processes on shared hosts. Thanks @aether-ai-agent.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import { execFileSync, execSync } from "node:child_process";
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import type { ApiContributor, Entry, MapConfig, User } from "./update-clawtributors.types.js";
|
||||
@@ -290,6 +290,27 @@ function parseCount(value: string): number {
|
||||
return /^\d+$/.test(value) ? Number(value) : 0;
|
||||
}
|
||||
|
||||
function isValidLogin(login: string): boolean {
|
||||
if (!/^[A-Za-z0-9-]{1,39}$/.test(login)) {
|
||||
return false;
|
||||
}
|
||||
if (login.startsWith("-") || login.endsWith("-")) {
|
||||
return false;
|
||||
}
|
||||
if (login.includes("--")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function normalizeLogin(login: string | null): string | null {
|
||||
if (!login) {
|
||||
return null;
|
||||
}
|
||||
const trimmed = login.trim();
|
||||
return isValidLogin(trimmed) ? trimmed : null;
|
||||
}
|
||||
|
||||
function normalizeAvatar(url: string): string {
|
||||
if (!/^https?:/i.test(url)) {
|
||||
return url;
|
||||
@@ -307,8 +328,12 @@ function isGhostAvatar(url: string): boolean {
|
||||
}
|
||||
|
||||
function fetchUser(login: string): User | null {
|
||||
const normalized = normalizeLogin(login);
|
||||
if (!normalized) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const data = execSync(`gh api users/${login}`, {
|
||||
const data = execFileSync("gh", ["api", `users/${normalized}`], {
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
@@ -334,45 +359,45 @@ function resolveLogin(
|
||||
emailToLogin: Record<string, string>,
|
||||
): string | null {
|
||||
if (email && emailToLogin[email]) {
|
||||
return emailToLogin[email];
|
||||
return normalizeLogin(emailToLogin[email]);
|
||||
}
|
||||
|
||||
if (email && name) {
|
||||
const guessed = guessLoginFromEmailName(name, email, apiByLogin);
|
||||
if (guessed) {
|
||||
return guessed;
|
||||
return normalizeLogin(guessed);
|
||||
}
|
||||
}
|
||||
|
||||
if (email && email.endsWith("@users.noreply.github.com")) {
|
||||
const local = email.split("@", 1)[0];
|
||||
const login = local.includes("+") ? local.split("+")[1] : local;
|
||||
return login || null;
|
||||
return normalizeLogin(login);
|
||||
}
|
||||
|
||||
if (email && email.endsWith("@github.com")) {
|
||||
const login = email.split("@", 1)[0];
|
||||
if (apiByLogin.has(login.toLowerCase())) {
|
||||
return login;
|
||||
return normalizeLogin(login);
|
||||
}
|
||||
}
|
||||
|
||||
const normalized = normalizeName(name);
|
||||
if (nameToLogin[normalized]) {
|
||||
return nameToLogin[normalized];
|
||||
return normalizeLogin(nameToLogin[normalized]);
|
||||
}
|
||||
|
||||
const compact = normalized.replace(/\s+/g, "");
|
||||
if (nameToLogin[compact]) {
|
||||
return nameToLogin[compact];
|
||||
return normalizeLogin(nameToLogin[compact]);
|
||||
}
|
||||
|
||||
if (apiByLogin.has(normalized)) {
|
||||
return normalized;
|
||||
return normalizeLogin(normalized);
|
||||
}
|
||||
|
||||
if (apiByLogin.has(compact)) {
|
||||
return compact;
|
||||
return normalizeLogin(compact);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user