Skip to content

Commit a8607fd

Browse files
authored
Merge pull request #1223 from jamessimone/main
feat: adds allow-list commands
2 parents 5274c3c + e5f7dd2 commit a8607fd

19 files changed

Lines changed: 867 additions & 20 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ package-lock.json
99
# debug logs
1010
npm-error.log
1111
yarn-error.log
12-
12+
stderr*.txt
13+
stdout*.txt
1314

1415
# compile source
1516
lib

.mocharc.json

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
{
2-
"require": [
3-
"ts-node/register"
4-
],
5-
"watch-extensions": "ts",
6-
"watch-files": [
7-
"src",
8-
"test"
9-
],
2+
"forbid-only": true,
3+
"node-option": ["loader=ts-node/esm"],
104
"recursive": true,
115
"reporter": "spec",
6+
"require": ["ts-node/register"],
127
"timeout": 10000,
13-
"node-option": [
14-
"loader=ts-node/esm"
15-
]
16-
}
8+
"watch-extensions": "ts",
9+
"watch-files": ["src", "test"]
10+
}

COMMANDS.md

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
## Commands
22

3-
validate a digital signature for a npm package
3+
validate a digital signature for a npm package and interact with the allowlist for unsigned packages
44

5-
- [`sfdx plugins:trust:verify -n <string> [-r <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`]
5+
- [`sf plugins trust verify`](#sf-plugins-trust-verify)
6+
- [`sf plugins trust allowlist add`](#sf-plugins-trust-allowlist-add)
7+
- [`sf plugins trust allowlist list`](#sf-plugins-trust-allowlist-list)
8+
- [`sf plugins trust allowlist remove`](#sf-plugins-trust-allowlist-remove)
69

7-
## `sfdx plugins:trust:verify -n <string> [-r <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`
10+
## `sf plugins trust verify`
811

912
```USAGE
1013
$ sfdx plugins:trust:verify -n <string> [-r <string>] [--json] [--loglevel
@@ -28,3 +31,80 @@ EXAMPLES
2831
sfdx plugins:trust:verify --npm @scope/npmName --registry https://npm.pkg.github.com
2932
sfdx plugins:trust:verify --npm @scope/npmName
3033
```
34+
35+
## `sf plugins trust allowlist add`
36+
37+
Add plugins to the unsigned plugin allow list.
38+
39+
```
40+
USAGE
41+
$ sf plugins trust allowlist add -n <value>... [--json] [--flags-dir <value>]
42+
43+
FLAGS
44+
-n, --name=<value>... The npm name of the plugin to add to the allow list. Multiple names can be added in one
45+
invocation by repeating the --name flag.
46+
47+
GLOBAL FLAGS
48+
--flags-dir=<value> Import flag values from a directory.
49+
--json Format output as json.
50+
51+
DESCRIPTION
52+
Add plugins to the unsigned plugin allow list.
53+
54+
Adds one or more plugins to the unsignedPluginAllowList.json file, creating the file if it doesn't exist. Plugins
55+
already present in the allow list are skipped.
56+
57+
EXAMPLES
58+
sf plugins trust allowlist add --name @scope/my-plugin
59+
60+
sf plugins trust allowlist add --name @scope/my-plugin --name another-plugin
61+
```
62+
63+
## `sf plugins trust allowlist list`
64+
65+
List the plugins on the unsigned plugin allowlist.
66+
67+
```
68+
USAGE
69+
$ sf plugins trust allowlist list [--json] [--flags-dir <value>]
70+
71+
GLOBAL FLAGS
72+
--flags-dir=<value> Import flag values from a directory.
73+
--json Format output as json.
74+
75+
DESCRIPTION
76+
List the plugins on the unsigned plugin allowlist.
77+
78+
Prints the contents of the unsignedPluginAllowList.json file as a table.
79+
80+
EXAMPLES
81+
sf plugins trust allowlist list
82+
```
83+
84+
## `sf plugins trust allowlist remove`
85+
86+
Remove plugins from the unsigned plugin allowlist.
87+
88+
```
89+
USAGE
90+
$ sf plugins trust allowlist remove -n <value>... [--json] [--flags-dir <value>]
91+
92+
FLAGS
93+
-n, --name=<value>... The npm name of the plugin to remove from the allowlist. Multiple names can be removed in one
94+
invocation by repeating the --name flag.
95+
96+
GLOBAL FLAGS
97+
--flags-dir=<value> Import flag values from a directory.
98+
--json Format output as json.
99+
100+
DESCRIPTION
101+
Remove plugins from the unsigned plugin allowlist.
102+
103+
Removes one or more plugins from the unsignedPluginAllowList.json file. Plugins not present in the allowlist are
104+
skipped.
105+
106+
EXAMPLES
107+
sf plugins trust allowlist remove --name @scope/my-plugin
108+
109+
sf plugins trust allowlist remove --name @scope/my-plugin --name another-plugin
110+
```

command-snapshot.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,30 @@
77
"flags": ["flags-dir", "json"],
88
"plugin": "@salesforce/plugin-trust"
99
},
10+
{
11+
"alias": [],
12+
"command": "plugins:trust:allowlist:add",
13+
"flagAliases": [],
14+
"flagChars": ["n"],
15+
"flags": ["flags-dir", "json", "name"],
16+
"plugin": "@salesforce/plugin-trust"
17+
},
18+
{
19+
"alias": [],
20+
"command": "plugins:trust:allowlist:list",
21+
"flagAliases": [],
22+
"flagChars": [],
23+
"flags": ["flags-dir", "json"],
24+
"plugin": "@salesforce/plugin-trust"
25+
},
26+
{
27+
"alias": [],
28+
"command": "plugins:trust:allowlist:remove",
29+
"flagAliases": [],
30+
"flagChars": ["n"],
31+
"flags": ["flags-dir", "json", "name"],
32+
"plugin": "@salesforce/plugin-trust"
33+
},
1034
{
1135
"alias": [],
1236
"command": "plugins:trust:verify",

messages/allowlist.add.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# summary
2+
3+
Add plugins to the plugin allowlist.
4+
5+
# description
6+
7+
The plugin allowlist lets users automatically install a plugin without being prompted, even when the plugin is unsigned.
8+
9+
This command adds one or more plugins to the `unsignedPluginAllowList.json` file, creating the file if it doesn't exist. Plugins already present in the allowlist are skipped.
10+
11+
# examples
12+
13+
- Add a single plugin to the allowlist:
14+
15+
<%= config.bin %> <%= command.id %> --name @scope/my-plugin
16+
17+
- Add multiple plugins to the allowlist:
18+
19+
<%= config.bin %> <%= command.id %> --name @scope/my-plugin --name another-plugin
20+
21+
# flags.name.summary
22+
23+
The npm name of the plugin to add to the allowlist. Add multiple plugins by specifying the `--name` flag multiple times.

messages/allowlist.list.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# summary
2+
3+
List the plugins on the plugin allowlist.
4+
5+
# description
6+
7+
The plugin allowlist lets users automatically install a plugin without being prompted, even when the plugin is unsigned.
8+
9+
This command prints the contents of the `unsignedPluginAllowList.json` file as a table.
10+
11+
# examples
12+
13+
- List all plugins on the allowlist:
14+
15+
<%= config.bin %> <%= command.id %>
16+
17+
# NoPluginsAllowListed
18+
19+
No plugins are currently on the allowlist.

messages/allowlist.remove.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# summary
2+
3+
Remove plugins from the plugin allowlist.
4+
5+
# description
6+
7+
The plugin allowlist lets users automatically install a plugin without being prompted, even when the plugin is unsigned.
8+
9+
This command removes one or more plugins from the `unsignedPluginAllowList.json` file. Plugins not present in the allowlist are skipped.
10+
11+
# examples
12+
13+
- Remove a single plugin from the allowlist:
14+
15+
<%= config.bin %> <%= command.id %> --name @scope/my-plugin
16+
17+
- Remove multiple plugins from the allowlist:
18+
19+
<%= config.bin %> <%= command.id %> --name @scope/my-plugin --name another-plugin
20+
21+
# flags.name.summary
22+
23+
The npm name of the plugin to remove from the allowlist. Remove multiple plugins by specifying the `--name` flag multiple times.

package.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,23 @@
7777
"description": "list installed plugins",
7878
"subtopics": {
7979
"trust": {
80-
"description": "validate a digital signature for a npm package"
80+
"description": "validate a digital signature for a npm package",
81+
"subtopics": {
82+
"allowlist": {
83+
"description": "manage the unsigned plugin allowlist",
84+
"subtopics": {
85+
"add": {
86+
"description": "adds to the allowlist"
87+
},
88+
"remove": {
89+
"description": "removes from the allowlist"
90+
},
91+
"list": {
92+
"description": "lists plugins in the allowlist"
93+
}
94+
}
95+
}
96+
}
8197
}
8298
}
8399
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2026, Salesforce, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
18+
import { Messages } from '@salesforce/core';
19+
20+
import { AllowList, type AllowListResult } from '../../../../shared/allowlist.js';
21+
22+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
23+
const messages = Messages.loadMessages('@salesforce/plugin-trust', 'allowlist.add');
24+
25+
export class AllowListAdd extends SfCommand<AllowListResult> {
26+
public static readonly summary = messages.getMessage('summary');
27+
public static readonly description = messages.getMessage('description');
28+
public static readonly examples = messages.getMessages('examples');
29+
30+
public static readonly flags = {
31+
name: Flags.string({
32+
char: 'n',
33+
summary: messages.getMessage('flags.name.summary'),
34+
required: true,
35+
multiple: true,
36+
}),
37+
};
38+
39+
public async run(): Promise<AllowListResult> {
40+
const { flags } = await this.parse(AllowListAdd);
41+
const allowList = new AllowList(this.config.configDir);
42+
const existingAllowList = await allowList.get();
43+
const results: AllowListResult = [];
44+
const addedPlugins: string[] = [];
45+
46+
for (const name of flags.name) {
47+
const shouldAddPlugin = !existingAllowList.includes(name);
48+
if (shouldAddPlugin) {
49+
addedPlugins.push(name);
50+
results.push({ Plugin: name, Status: 'added' });
51+
} else {
52+
results.push({ Plugin: name, Status: 'skipped', Reason: 'already within allowlist' });
53+
}
54+
}
55+
56+
if (addedPlugins.length > 0) {
57+
await allowList.save([...existingAllowList, ...addedPlugins]);
58+
}
59+
60+
this.table({
61+
data: results,
62+
});
63+
64+
return results;
65+
}
66+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2026, Salesforce, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { SfCommand } from '@salesforce/sf-plugins-core';
18+
import { Messages } from '@salesforce/core';
19+
20+
import { AllowList, type AllowListResult } from '../../../../shared/allowlist.js';
21+
22+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
23+
const messages = Messages.loadMessages('@salesforce/plugin-trust', 'allowlist.list');
24+
25+
export class AllowListList extends SfCommand<AllowListResult> {
26+
public static readonly summary = messages.getMessage('summary');
27+
public static readonly description = messages.getMessage('description');
28+
public static readonly examples = messages.getMessages('examples');
29+
30+
public async run(): Promise<AllowListResult> {
31+
const existingAllowList = await new AllowList(this.config.configDir).get();
32+
33+
if (existingAllowList.length === 0) {
34+
this.log(messages.getMessage('NoPluginsAllowListed'));
35+
return [];
36+
}
37+
38+
const results: AllowListResult = existingAllowList.map((plugin) => ({ Plugin: plugin }));
39+
40+
this.table({
41+
data: results,
42+
});
43+
44+
return results;
45+
}
46+
}

0 commit comments

Comments
 (0)