feat(coding-agent): add project trust gating
This commit is contained in:
@@ -963,6 +963,7 @@ export class DefaultPackageManager implements PackageManager {
|
||||
async install(source: string, options?: { local?: boolean }): Promise<void> {
|
||||
const parsed = this.parseSource(source);
|
||||
const scope: SourceScope = options?.local ? "project" : "user";
|
||||
this.assertProjectTrustedForScope(scope);
|
||||
await this.withProgress("install", source, `Installing ${source}...`, async () => {
|
||||
if (parsed.type === "npm") {
|
||||
await this.installNpm(parsed, scope, false);
|
||||
@@ -991,6 +992,7 @@ export class DefaultPackageManager implements PackageManager {
|
||||
async remove(source: string, options?: { local?: boolean }): Promise<void> {
|
||||
const parsed = this.parseSource(source);
|
||||
const scope: SourceScope = options?.local ? "project" : "user";
|
||||
this.assertProjectTrustedForScope(scope);
|
||||
await this.withProgress("remove", source, `Removing ${source}...`, async () => {
|
||||
if (parsed.type === "npm") {
|
||||
await this.uninstallNpm(parsed, scope);
|
||||
@@ -1673,6 +1675,12 @@ export class DefaultPackageManager implements PackageManager {
|
||||
return { name, version };
|
||||
}
|
||||
|
||||
private assertProjectTrustedForScope(scope: SourceScope): void {
|
||||
if (scope === "project" && !this.settingsManager.isProjectTrusted()) {
|
||||
throw new Error("Project is not trusted; refusing to access project package storage");
|
||||
}
|
||||
}
|
||||
|
||||
private getNpmCommand(): { command: string; args: string[] } {
|
||||
const configuredCommand = this.settingsManager.getNpmCommand();
|
||||
if (!configuredCommand || configuredCommand.length === 0) {
|
||||
@@ -1893,6 +1901,7 @@ export class DefaultPackageManager implements PackageManager {
|
||||
return this.getTemporaryDir("npm");
|
||||
}
|
||||
if (scope === "project") {
|
||||
this.assertProjectTrustedForScope(scope);
|
||||
return join(this.cwd, CONFIG_DIR_NAME, "npm");
|
||||
}
|
||||
return join(this.agentDir, "npm");
|
||||
@@ -1933,6 +1942,7 @@ export class DefaultPackageManager implements PackageManager {
|
||||
return join(this.getTemporaryDir("npm"), "node_modules", source.name);
|
||||
}
|
||||
if (scope === "project") {
|
||||
this.assertProjectTrustedForScope(scope);
|
||||
return join(this.cwd, CONFIG_DIR_NAME, "npm", "node_modules", source.name);
|
||||
}
|
||||
return join(this.agentDir, "npm", "node_modules", source.name);
|
||||
@@ -1971,6 +1981,7 @@ export class DefaultPackageManager implements PackageManager {
|
||||
return undefined;
|
||||
}
|
||||
if (scope === "project") {
|
||||
this.assertProjectTrustedForScope(scope);
|
||||
return join(this.cwd, CONFIG_DIR_NAME, "git");
|
||||
}
|
||||
return join(this.agentDir, "git");
|
||||
@@ -1996,6 +2007,7 @@ export class DefaultPackageManager implements PackageManager {
|
||||
|
||||
private getBaseDirForScope(scope: SourceScope): string {
|
||||
if (scope === "project") {
|
||||
this.assertProjectTrustedForScope(scope);
|
||||
return join(this.cwd, CONFIG_DIR_NAME);
|
||||
}
|
||||
if (scope === "user") {
|
||||
@@ -2257,9 +2269,10 @@ export class DefaultPackageManager implements PackageManager {
|
||||
themes: join(projectBaseDir, "themes"),
|
||||
};
|
||||
const userAgentsSkillsDir = join(getHomeDir(), ".agents", "skills");
|
||||
const projectAgentsSkillDirs = collectAncestorAgentsSkillDirs(this.cwd).filter(
|
||||
(dir) => resolve(dir) !== resolve(userAgentsSkillsDir),
|
||||
);
|
||||
const projectTrusted = this.settingsManager.isProjectTrusted();
|
||||
const projectAgentsSkillDirs = projectTrusted
|
||||
? collectAncestorAgentsSkillDirs(this.cwd).filter((dir) => resolve(dir) !== resolve(userAgentsSkillsDir))
|
||||
: [];
|
||||
|
||||
const addResources = (
|
||||
resourceType: ResourceType,
|
||||
@@ -2275,23 +2288,25 @@ export class DefaultPackageManager implements PackageManager {
|
||||
}
|
||||
};
|
||||
|
||||
// Project extensions from .pi/
|
||||
addResources(
|
||||
"extensions",
|
||||
collectAutoExtensionEntries(projectDirs.extensions),
|
||||
projectMetadata,
|
||||
projectOverrides.extensions,
|
||||
projectBaseDir,
|
||||
);
|
||||
if (projectTrusted) {
|
||||
// Project extensions from .pi/
|
||||
addResources(
|
||||
"extensions",
|
||||
collectAutoExtensionEntries(projectDirs.extensions),
|
||||
projectMetadata,
|
||||
projectOverrides.extensions,
|
||||
projectBaseDir,
|
||||
);
|
||||
|
||||
// Project skills from .pi/
|
||||
addResources(
|
||||
"skills",
|
||||
collectAutoSkillEntries(projectDirs.skills, "pi"),
|
||||
projectMetadata,
|
||||
projectOverrides.skills,
|
||||
projectBaseDir,
|
||||
);
|
||||
// Project skills from .pi/
|
||||
addResources(
|
||||
"skills",
|
||||
collectAutoSkillEntries(projectDirs.skills, "pi"),
|
||||
projectMetadata,
|
||||
projectOverrides.skills,
|
||||
projectBaseDir,
|
||||
);
|
||||
}
|
||||
|
||||
// Project skills from .agents/ (each with its own baseDir)
|
||||
for (const agentsSkillsDir of projectAgentsSkillDirs) {
|
||||
@@ -2309,20 +2324,22 @@ export class DefaultPackageManager implements PackageManager {
|
||||
);
|
||||
}
|
||||
|
||||
addResources(
|
||||
"prompts",
|
||||
collectAutoPromptEntries(projectDirs.prompts),
|
||||
projectMetadata,
|
||||
projectOverrides.prompts,
|
||||
projectBaseDir,
|
||||
);
|
||||
addResources(
|
||||
"themes",
|
||||
collectAutoThemeEntries(projectDirs.themes),
|
||||
projectMetadata,
|
||||
projectOverrides.themes,
|
||||
projectBaseDir,
|
||||
);
|
||||
if (projectTrusted) {
|
||||
addResources(
|
||||
"prompts",
|
||||
collectAutoPromptEntries(projectDirs.prompts),
|
||||
projectMetadata,
|
||||
projectOverrides.prompts,
|
||||
projectBaseDir,
|
||||
);
|
||||
addResources(
|
||||
"themes",
|
||||
collectAutoThemeEntries(projectDirs.themes),
|
||||
projectMetadata,
|
||||
projectOverrides.themes,
|
||||
projectBaseDir,
|
||||
);
|
||||
}
|
||||
|
||||
// User extensions from ~/.pi/agent/
|
||||
addResources(
|
||||
|
||||
Reference in New Issue
Block a user