Releases: ddlin/vocareum-publisher
Release list
v1.3.7
Security
- Directory scaffolding no longer writes
.gitkeepthrough a symlink. The configured-directory scaffolding added in 1.3.6 (pull --contentand orphan import) used an unguarded write, so a configured directory that was a symlink escaping the workspace (e.g.docs -> /outside) would get its.gitkeepwritten outside the repository. Scaffolding now skips — with a warning — any configured directory whose final path component is a symlink, matching the confinement the content read/restore paths already enforce (the part base is realpath-confined by the caller, so guarding the final segment closes the escape).
Full changelog: https://github.com/ddlin/vocareum-publisher/blob/v1.3.7/CHANGELOG.md
v1.3.6
Fixed
pull --contentnow restores an entirely-deleted assignment/part directory. When the whole part directory was gone, the per-file confinement check ranrealpath()on the missing base, threwENOENT, and returned "not confined" — so every remote file was mislabeled as an escaping symlink and skipped, and nothing was restored. Detection now recognizes a missing base (no local files or symlinks to read through) and treats every remote file as a fresh add; apply recreates the base and confines each individual write.
Added
pull --contentscaffolds the part's configured directories after a content restore, matching a fresh import: any declared directory that is empty on the remote is recreated with a.gitkeep, while directories that received content are left untouched. Note: deleting an already-empty subdirectory is not restored, because it produces no content drift to trigger the pull.
Full changelog: https://github.com/ddlin/vocareum-publisher/blob/v1.3.6/CHANGELOG.md
v1.3.5
Fixed
pull --contentcan again restore a deleted directory in an assignment that also contains a symlink. Previously, a single remote file whose local path was (or resolved through) a symlink aborted the content drift check for the entire assignment — so a separately-deleted directory in that assignment was never detected or re-pulled, and the only recovery was to dropvocareum.yamland re-init. This affected both symlinks escaping the part directory (e.g.docs/README.html→ the sharedcourse/≡lib/tree) and in-part symlinks, whichwriteFileUnderBaserefuses to overwrite regardless of target. vocgit now skips just the symlinked file (with a per-file warning) and still compares/restores the rest of the part.
Security
- Hardened the content-drift scan and the stored remote-file map against symlinks. The remote-file map persisted for apply now excludes any file whose local path escapes the part directory or is itself a symlink, so a restore is never aborted by
writeFileUnderBaserejecting the path. The deleted-file detector additionally skips any directory or nested entry whose path resolves outside the part directory, so apply'sunlinkcan never follow a symlink and delete a file outside the workspace.
Full changelog: https://github.com/ddlin/vocareum-publisher/blob/v1.3.5/CHANGELOG.md
v1.3.4
Changed
- Push settings-update fallbacks now log the Vocareum API's actual 400 message (
[API: …]) when a part or assignment settingsPUTis rejected and vocgit retries with a reduced payload. Previously the warning only said "rejected", hiding which field the API objected to (e.g. re-sending create-only lab fields likelabtype/container_image/lab_interfaceon Databricks/cloud parts). Diagnostic only — the retry ladder's behavior is unchanged.
Full changelog: https://github.com/ddlin/vocareum-publisher/blob/v1.3.4/CHANGELOG.md
v1.3.3
Fixed
- Orphan import no longer fails with a bogus symlink error:
vocgit pull
imports failed withInvalid path: "..." escapes base directory through a symlinkfor every orphaned assignment because the symlink-hardened write
path treated a not-yet-created import directory as an escape (realpathon
the missing base).writeFileUnderBasenow creates the trusted base
directory before running its confinement checks, restoring fresh imports
while keeping all symlink-escape rejections intact. lti_urlclassified as a non-setting: Vocareum returns a server-derived
LTI launch URL on assignment reads. It is identity metadata (encodes the
course/assignment IDs), not an instructor-configurable setting, so it is now
dropped likepart_urlinstead of being preserved under_unknown_settings
and flagged in the end-of-run unsupported-fields report.
Security
- The user-typed import directory name is now confined to the workspace with
realpath-based validation (assertConfinedToWorkspace) before any directory
creation. A lexical-only check was insufficient: a name beneath an
in-workspace symlink that points outside the workspace would pass the lexical
test and let the import write outside the working tree. - Removed an accidental self-dependency (
vocareum-publisherlisted in its own
dependencies) that caused a nested copy of the package to be installed.
v1.3.2
CLI: vocareum-publisher@1.3.2 (npm) — Stage 1a internal service-layer refactor; no user-facing change (same CLI flags, output, exit codes, and API behavior). See CHANGELOG.md.
VS Code extension: the attached vocgit-1.1.0.vsix is the current extension build (the extension is versioned independently of the CLI). Install with code --install-extension vocgit-1.1.0.vsix — see vscode-extension/README.md.
v1.2.0
CLI: vocareum-publisher@1.2.0 (npm) — see CHANGELOG.md for details.
VS Code extension: the attached vocgit-1.1.0.vsix is the current extension build (extension versions move independently of the CLI). Install with code --install-extension vocgit-1.1.0.vsix — see vscode-extension/README.md for details.