mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-08 01:02:55 +00:00
fix: allow admins to approve dependency guard (#88966)
* fix: allow admins to approve dependency guard * fix: auto-bypass trusted dependency authors
This commit is contained in:
@@ -5,22 +5,26 @@ import {
|
||||
createAutoscrubCommit,
|
||||
dependencyGuardCommentAuthors,
|
||||
dependencyGuardCommentHeadSha,
|
||||
dependencyGuardTrustedActorCandidates,
|
||||
dependencyFieldChanges,
|
||||
dependencyOverrideExpectedSha,
|
||||
findDependencyOverrideCommand,
|
||||
findDependencyOverrideCommandAsync,
|
||||
findTrustedDependencyGuardActor,
|
||||
githubApi,
|
||||
isAutoscrubbedDependencyComment,
|
||||
isDependencyGuardAuthorizedForHead,
|
||||
isDependencyFile,
|
||||
isDependencyGuardMarkerComment,
|
||||
isDependencyManifest,
|
||||
isDependencyGuardTrustedForHead,
|
||||
isPackageLockfile,
|
||||
readBoundedGitHubErrorText,
|
||||
renderAuthorizedDependencyComment,
|
||||
renderAutoscrubbedDependencyComment,
|
||||
renderBlockedDependencyComment,
|
||||
renderClearedDependencyGuardComment,
|
||||
renderTrustedDependencyComment,
|
||||
sanitizeDisplayValue,
|
||||
securityApproverSet,
|
||||
shouldAutoscrubDependencyLockfiles,
|
||||
@@ -142,6 +146,89 @@ describe("dependency guard script", () => {
|
||||
).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it("accepts repository admins through the same sha-bound override command", async () => {
|
||||
const comments = [
|
||||
{
|
||||
body: "/allow-dependencies-change admin reviewed",
|
||||
created_at: "2026-05-28T20:03:00Z",
|
||||
html_url: "https://example.test/comment",
|
||||
user: { login: "repo-admin" },
|
||||
},
|
||||
];
|
||||
|
||||
await expect(
|
||||
findDependencyOverrideCommandAsync({
|
||||
comments,
|
||||
expectedSha: headSha,
|
||||
isSecurityMember: async (login) => login === "repo-admin",
|
||||
newerThan: "2026-05-28T20:02:00Z",
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
login: "repo-admin",
|
||||
reason: "admin reviewed",
|
||||
sha: headSha,
|
||||
url: "https://example.test/comment",
|
||||
});
|
||||
});
|
||||
|
||||
it("recognizes trusted dependency guard actors automatically", async () => {
|
||||
const sameActorCandidates = dependencyGuardTrustedActorCandidates({
|
||||
pullRequest: { user: { login: "repo-admin" } },
|
||||
event: { pull_request: { head: { sha: headSha } }, sender: { login: "repo-admin" } },
|
||||
currentHeadSha: headSha,
|
||||
});
|
||||
const untrustedAuthorCandidate = dependencyGuardTrustedActorCandidates({
|
||||
pullRequest: { user: { login: "contributor" } },
|
||||
event: { after: headSha, sender: { login: "security-user" } },
|
||||
currentHeadSha: headSha,
|
||||
});
|
||||
const staleAuthorCandidate = dependencyGuardTrustedActorCandidates({
|
||||
pullRequest: { user: { login: "repo-admin" } },
|
||||
event: { pull_request: { head: { sha: staleSha } }, sender: { login: "repo-admin" } },
|
||||
currentHeadSha: headSha,
|
||||
});
|
||||
|
||||
expect(sameActorCandidates).toEqual([{ login: "repo-admin", source: "pull request author" }]);
|
||||
expect(untrustedAuthorCandidate).toEqual([
|
||||
{ login: "contributor", source: "pull request author" },
|
||||
]);
|
||||
expect(staleAuthorCandidate).toEqual([]);
|
||||
|
||||
await expect(
|
||||
findTrustedDependencyGuardActor({
|
||||
candidates: untrustedAuthorCandidate,
|
||||
isDependencyApprover: async (login) =>
|
||||
login === "security-user" || login === "repo-admin" ? "openclaw-secops" : null,
|
||||
}),
|
||||
).resolves.toBeNull();
|
||||
await expect(
|
||||
findTrustedDependencyGuardActor({
|
||||
candidates: sameActorCandidates,
|
||||
isDependencyApprover: async (login) => (login === "repo-admin" ? "repository admin" : null),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
login: "repo-admin",
|
||||
reason: "pull request author; repository admin",
|
||||
});
|
||||
});
|
||||
|
||||
it("renders trusted dependency graph comments without blocker language", () => {
|
||||
const body = renderTrustedDependencyComment({
|
||||
actor: { login: "repo-admin", reason: "pull request author; repository admin" },
|
||||
headSha,
|
||||
});
|
||||
|
||||
expect(body).toContain("<!-- openclaw:dependency-graph-guard -->");
|
||||
expect(body).toContain("Dependency graph changes noted");
|
||||
expect(body).toContain("informational");
|
||||
expect(body).toContain("@repo-admin");
|
||||
expect(body).toContain(headSha);
|
||||
expect(body).not.toContain("are blocked");
|
||||
expect(body).not.toContain("/allow-dependencies-change");
|
||||
expect(isDependencyGuardTrustedForHead({ body }, headSha)).toBe(true);
|
||||
expect(isDependencyGuardTrustedForHead({ body }, staleSha)).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects override commands without a freshness barrier", () => {
|
||||
const override = findDependencyOverrideCommand({
|
||||
comments: [
|
||||
|
||||
@@ -221,6 +221,17 @@ describe("dependency guard workflow", () => {
|
||||
expect(autoscrubCommentIndex).toBeGreaterThan(deleteCommentIndex);
|
||||
});
|
||||
|
||||
it("checks trusted actors before autoscrub can mutate dependency changes", () => {
|
||||
const script = readFileSync("scripts/github/dependency-guard.mjs", "utf8");
|
||||
const trustedActorIndex = script.indexOf("const trustedActor =");
|
||||
const autoscrubCandidateIndex = script.indexOf("const autoscrubCandidate =");
|
||||
const autoscrubOutputIndex = script.indexOf('await setOutput("autoscrub", "true")');
|
||||
|
||||
expect(trustedActorIndex).toBeGreaterThan(0);
|
||||
expect(autoscrubCandidateIndex).toBeGreaterThan(trustedActorIndex);
|
||||
expect(autoscrubOutputIndex).toBeGreaterThan(trustedActorIndex);
|
||||
});
|
||||
|
||||
it("requires secops review for future workflow or guard changes", () => {
|
||||
const codeowners = readFileSync(CODEOWNERS, "utf8");
|
||||
expect(codeowners).toContain(
|
||||
|
||||
Reference in New Issue
Block a user