fix(security): harden skill apply and activation flows

Restrict auto-apply to trusted review comments so spoofed issue comments
cannot write optimized SKILL.md content into pull request branches.

Reject activation symlinks that escape the source root and add
regression coverage for both security checks.
This commit is contained in:
sickn33
2026-03-26 13:23:54 +01:00
parent b8684488ab
commit bc49ceec90
5 changed files with 161 additions and 12 deletions

View File

@@ -10,6 +10,7 @@ const scriptPath = path.join(repoRoot, "scripts", "activate-skills.sh");
const root = fs.mkdtempSync(path.join(os.tmpdir(), "activate-skills-shell-"));
const baseDir = path.join(root, "antigravity");
const repoSkills = path.join(root, "repo-skills");
const outsideDir = path.join(root, "outside-skill-root");
function makeSkill(skillId) {
const skillDir = path.join(repoSkills, skillId);
@@ -25,6 +26,9 @@ try {
makeSkill("brainstorming");
makeSkill("systematic-debugging");
makeSkill("custom-skill");
fs.mkdirSync(outsideDir, { recursive: true });
fs.writeFileSync(path.join(outsideDir, "secret.txt"), "outside", "utf8");
fs.symlinkSync(outsideDir, path.join(repoSkills, "escape-link"), "dir");
const result = spawnSync(
"bash",
@@ -54,6 +58,14 @@ try {
fs.existsSync(path.join(baseDir, "skills_library", "brainstorming", "SKILL.md")),
"repo skills should be synced into the backing library",
);
assert.ok(
!fs.existsSync(path.join(baseDir, "skills_library", "escape-link")),
"repo sync must not copy symlinked skills that point outside the source root",
);
assert.ok(
!fs.existsSync(path.join(baseDir, "skills", "escape-link")),
"unsafe symlinked skills must never become active",
);
assert.match(
result.stdout,
/Done! Antigravity skills are now activated\./,

View File

@@ -0,0 +1,43 @@
const assert = require('node:assert');
const path = require('node:path');
const repoRoot = path.resolve(__dirname, '..', '..', '..');
const {
selectLatestTrustedReviewComment,
} = require(path.join(repoRoot, 'tools', 'scripts', 'apply_skill_optimization.cjs'));
const trustedMaintainerComment = {
body: '## Tessl Skill Review\ntrusted maintainer content',
author_association: 'MEMBER',
user: { login: 'maintainer-user', type: 'User' },
};
const spoofedUserComment = {
body: '## Tessl Skill Review\nspoofed content',
author_association: 'NONE',
user: { login: 'random-user', type: 'User' },
};
const trustedBotComment = {
body: '## Tessl Skill Review\nbot content',
author_association: 'NONE',
user: { login: 'github-actions[bot]', type: 'Bot' },
};
assert.strictEqual(
selectLatestTrustedReviewComment([trustedMaintainerComment, spoofedUserComment]),
trustedMaintainerComment,
'the apply script must ignore later untrusted review comments',
);
assert.strictEqual(
selectLatestTrustedReviewComment([spoofedUserComment, trustedBotComment]),
trustedBotComment,
'the apply script should accept review comments from the configured trusted bot',
);
assert.strictEqual(
selectLatestTrustedReviewComment([spoofedUserComment]),
null,
'the apply script must reject untrusted review comments entirely',
);

View File

@@ -11,6 +11,7 @@ const LOCAL_TEST_COMMANDS = [
[path.join(TOOL_TESTS, "activate_skills_shell.test.js")],
[path.join(TOOL_TESTS, "activate_skills_batch_security.test.js")],
[path.join(TOOL_TESTS, "automation_workflows.test.js")],
[path.join(TOOL_TESTS, "apply_skill_optimization_security.test.js")],
[path.join(TOOL_TESTS, "build_catalog_bundles.test.js")],
[path.join(TOOL_TESTS, "claude_plugin_marketplace.test.js")],
[path.join(TOOL_TESTS, "installer_antigravity_guidance.test.js")],