Skip to content

Commit d500d79

Browse files
Sergio SisternesCopilot
andcommitted
fix: address Copilot review feedback — docs accuracy, path security, crash guard
- Fix docs claiming #ref overrides unversioned plugins (it does not) - Fix docs claiming git tag fallback for plugins without versions - Remove -p alias from docs (not in CLI implementation) - Add resolved_version to lockfile-spec documentation - Add --plugin flag to skill file command reference - Remove unused PluginNotFoundError import in view.py - Guard caret pin hint against non-semver version entries - Wrap _expand_specifier in try/except for raw git ref version_specs - Add path traversal protection via ensure_path_within for source.path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent fd6e787 commit d500d79

File tree

7 files changed

+21
-12
lines changed

7 files changed

+21
-12
lines changed

docs/src/content/docs/guides/marketplaces.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ apm install code-review@acme-plugins#~2.1.0
162162
apm install code-review@acme-plugins#>=1.0.0,<3.0.0
163163
```
164164

165-
The `#` separator carries a version specifier when the plugin declares `versions`, or a raw git ref when it does not. Plugins without `versions` continue to work as before.
165+
The `#` separator carries a version specifier only when the plugin declares `versions`. For plugins without `versions`, APM uses the source defined in the marketplace manifest, including any `source.ref` value; `#<ref>` does not override unversioned entries.
166166

167-
APM resolves the plugin name against the marketplace index, fetches the underlying Git repository at the resolved ref, and installs it as a standard APM dependency. The resolved source appears in `apm.yml` and `apm.lock.yaml` just like any direct dependency.
167+
APM resolves the plugin name against the marketplace index, fetches the underlying Git repository using the ref defined by the selected marketplace entry, and installs it as a standard APM dependency. The resolved source appears in `apm.yml` and `apm.lock.yaml` just like any direct dependency.
168168

169169
For full `apm install` options, see [CLI Commands](../../reference/cli-commands/).
170170

@@ -176,7 +176,7 @@ Show available versions for a marketplace plugin:
176176
apm view code-review@acme-plugins
177177
```
178178

179-
Displays a table of versions with their refs, sorted newest-first. For plugins without `versions`, shows remote tags and branches.
179+
Displays a table of versions with their refs, sorted newest-first. Plugins without `versions` show a "no version history" message.
180180

181181
## Provenance tracking
182182

docs/src/content/docs/reference/cli-commands.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ apm view microsoft/apm-sample-package -g
653653
- Shows package name, version, description, source, install path, context files, workflows, and hooks
654654
- `versions` lists remote tags and branches without cloning the repository
655655
- `versions` does not require the package to be installed locally
656-
- `NAME@MARKETPLACE` syntax shows the plugin's declared `versions` array sorted newest-first; for plugins without `versions`, falls back to remote tags and branches
656+
- `NAME@MARKETPLACE` syntax shows the plugin's declared `versions` array sorted newest-first; plugins without `versions` show no version history
657657

658658
### `apm outdated` - Check locked dependencies for updates
659659

@@ -1079,7 +1079,7 @@ apm marketplace publish [OPTIONS]
10791079

10801080
**Options:**
10811081
- `-m, --marketplace TEXT` - Target marketplace name
1082-
- `-p, --plugin TEXT` - Plugin name in the marketplace (default: `name` from `apm.yml`)
1082+
- `--plugin TEXT` - Plugin name in the marketplace (default: `name` from `apm.yml`)
10831083
- `--version TEXT` - Version to publish as semver `X.Y.Z` (default: `version` from `apm.yml`)
10841084
- `--ref TEXT` - Git ref or commit SHA (default: current HEAD)
10851085
- `--force` - Overwrite existing version entry with a different ref

docs/src/content/docs/reference/lockfile-spec.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ fields:
126126
| `deployed_files` | array of strings | MUST | Every file path APM deployed for this dependency, relative to project root. |
127127
| `source` | string | MAY | Dependency source. `"local"` for local path dependencies. Omitted for remote (git) dependencies. |
128128
| `version_spec` | string | MAY | Original semver range from the install specifier (e.g., `"^2.0.0"`). Present only for marketplace dependencies installed with a version constraint. Used by `apm outdated` to evaluate updates within the pinned range. |
129+
| `resolved_version` | string | MAY | Concrete version selected after marketplace semver resolution (e.g., `"2.3.1"`). Present only when APM resolved a marketplace dependency from a version constraint. Omitted for raw git refs and unversioned plugins. |
129130
| `local_path` | string | MAY | Filesystem path (relative or absolute) to the local package. Present only when `source` is `"local"`. |
130131

131132
Fields with empty or default values (empty strings, `false` booleans, empty

packages/apm-guide/.apm/skills/apm-usage/commands.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
| `apm marketplace browse NAME` | Browse marketplace packages | -- |
5858
| `apm marketplace update [NAME]` | Update marketplace index | -- |
5959
| `apm marketplace remove NAME` | Remove a marketplace | `-y` skip confirm |
60-
| `apm marketplace publish` | Publish version to marketplace.json | `-m MARKETPLACE`, `--version`, `--ref`, `--force`, `--dry-run` |
60+
| `apm marketplace publish` | Publish version to marketplace.json | `-m MARKETPLACE`, `--plugin`, `--version`, `--ref`, `--force`, `--dry-run` |
6161
| `apm marketplace validate NAME` | Validate marketplace manifest | `--check-refs`, `-v` |
6262
| `apm search QUERY@MARKETPLACE` | Search marketplace | `--limit N` |
6363
| `apm install NAME@MKT#^X.Y.Z` | Install with semver range | Supports `^`, `~`, `>=`, exact |

src/apm_cli/commands/marketplace.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,10 @@ def publish(marketplace_name, version_str, ref, plugin_name, dry_run, force, ver
895895

896896
marketplace_file = os.path.join(local_repo, source.path)
897897

898+
# Validate path to prevent traversal attacks via malicious source.path
899+
from ..utils.path_security import ensure_path_within
900+
ensure_path_within(Path(marketplace_file), Path(local_repo))
901+
898902
if not os.path.isfile(marketplace_file):
899903
logger.error(
900904
f"marketplace.json not found at expected path: "

src/apm_cli/commands/outdated.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,10 @@ def _check_marketplace_versions(dep, verbose):
143143
# Check version_spec if available (field added by parallel task)
144144
version_spec = getattr(dep, "version_spec", None)
145145
if version_spec:
146-
constraints = _expand_specifier(version_spec)
146+
try:
147+
constraints = _expand_specifier(version_spec)
148+
except ValueError:
149+
constraints = None
147150
if constraints and not _version_matches(best_parsed, constraints):
148151
# Find best version within the range
149152
best_in_range = None

src/apm_cli/commands/view.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ def _display_marketplace_versions(
253253
Fetches the marketplace manifest, finds the plugin, and renders its
254254
``versions[]`` array as a Rich table (with plain-text fallback).
255255
"""
256-
from ..marketplace.errors import MarketplaceFetchError, PluginNotFoundError
256+
from ..marketplace.errors import MarketplaceFetchError
257257
from ..marketplace.models import MarketplaceSource
258258
from ..marketplace.registry import get_marketplace_by_name
259259
from ..marketplace.client import fetch_or_cache
@@ -334,10 +334,11 @@ def _display_marketplace_versions(
334334
console.print(table)
335335
click.echo("")
336336
click.echo(f" Install: apm install {plugin_name}@{marketplace_name}")
337-
click.echo(
338-
f" Pin: apm install {plugin_name}@{marketplace_name}"
339-
f"#^{sorted_versions[0].version}"
340-
)
337+
if latest_version:
338+
click.echo(
339+
f" Pin: apm install {plugin_name}@{marketplace_name}"
340+
f"#^{latest_version}"
341+
)
341342

342343
except ImportError:
343344
# Plain-text fallback

0 commit comments

Comments
 (0)