Skip to content

fix: security hardening and provider robustness#288

Merged
mcanouil merged 26 commits intomainfrom
fix/security-hardening-and-provider-robustness
Feb 22, 2026
Merged

fix: security hardening and provider robustness#288
mcanouil merged 26 commits intomainfrom
fix/security-hardening-and-provider-robustness

Conversation

@mcanouil
Copy link
Copy Markdown
Owner

Summary

  • Harden tar extraction with deferred error pattern and hard link rejection.
  • Track additional extension install failures separately from primary install success.
  • Make retry backoff cancellable via AbortSignal.
  • Detect cross-platform absolute paths (Windows drive letters, UNC) in source prompts.
  • Skip internet check for local installs.
  • Centralise workspace schema index with TTL cache and in-flight deduplication.
  • Exclude fenced code block bodies from attribute and shortcode detection to prevent false diagnostics and completions inside code cells.

Test plan

  • All 514 VS Code extension tests pass.
  • All 386 core package tests pass.
  • TypeScript compilation clean.
  • ESLint clean.
  • New tests cover: hard link rejection, partial install failures, cancellable retry, cross-platform paths, code block range detection (27 tests), element attribute code block exclusion (7 tests), shortcode code block exclusion (4 tests).

…xtensionRoots

Reject all symlinks during TAR extraction (matching ZIP policy) to
eliminate multi-hop chain and TOCTOU attack vectors.
Add depth limit to findAllExtensionRoots inner search to prevent
stack overflow from deeply nested directories.
Remove unused checkSymlinkTarget function and its documentation.
Add getErrorMessage(error: unknown): string utility to @quarto-wizard/core
and replace ~29 inline occurrences of the error instanceof Error pattern
across all packages and the extension source.
…ns block

Cancel pending debounced validation before clearing diagnostics when a
document closes, preventing stale validation from firing.
Add a warning diagnostic when the extensions block is an array instead
of an object.
…ests

Extract REGISTRY_FETCH_TIMEOUT_MS and NETWORK_CHECK_TIMEOUT_MS to
constants, surface registry fetch errors to users, populate warning
message for malformed quarto-required format, and dynamically verify
all declared commands are registered in test suite.
Add sourceType override to install and use options so registry-based
installations write source-type as registry instead of github.
Display source type in the extension tree view.
Show Update action for both github and registry source types, and
show Reinstall for any extension with a known source.
Pass effectiveSourceType to installQuartoExtension in update, reinstall,
and updateAll commands so registry extensions retain source-type after
updates. Pass sourceType from the installed extension manifest in
applyUpdates.

Remove hasPinnedSourceRef and the quartoExtensionItemPinned context
value entirely. Since only registry extensions have update detection,
showing the Update button for all extensions with a known source is
harmless and avoids blocking registry updates.

Add missing installedExtensionsCache utility file that was referenced
in committed code but not included.
Store entryCount when writing the filesystem cache and verify it on
read. Reject cached data that is empty, has a mismatched entry count,
or is missing required fields on spot-checked entries.

This prevents stale or corrupted cache files (e.g. a cache with only
2 entries instead of the full registry) from silently breaking update
detection.
Extract shared isValidGitHubReference() function to replace the loose
includes("/") check in promptForGitHubReference(). The URI handler
already used a strict regex; both paths now use the same validator.
Wrap checkUpdate() with a pendingCheck guard to prevent concurrent
calls from resetting folderCache.latestVersions mid-population.
Follows the same pattern used by refreshAllExtensionsDataAsync().
Capture the error object in the catch block and log it via
getErrorMessage(). Changes log wording from "not found" to
"not accessible" for diagnostics while keeping user-facing
message unchanged.
Add a completed guard to the installFromSource cancellation callback,
matching the pattern already used in the batch install path.
Replace direct throws from tar callbacks with a stored SecurityError
that is re-thrown after tar.extract() completes.
This prevents unhandled rejections inside the tar library's internal
pipeline.
Also reject hard links (Link entries) alongside symbolic links.
Report partial failures via a new additionalInstallFailures field so
callers can distinguish a successful primary install from failed
additional installs.
The result.success flag now reflects only the primary install.
The sleep helper now accepts an optional AbortSignal so callers can
cancel a fetch during the retry delay instead of waiting for the next
attempt to start.
Export isAbsolutePathForAnyPlatform to handle Windows drive letters and
UNC paths.
Move the internet check after the source picker so local installs no
longer require a network connection.
Log partial install failures as warnings instead of errors.
Extract the duplicated schema-map-building logic from three YAML
providers into a shared workspaceSchemaIndex module with TTL-based
caching and in-flight request deduplication.
Guard the cache against concurrent invalidation races.
Add getCodeBlockRanges/isInCodeBlockRange utilities and thread them
through attribute, shortcode, and inline-diagnostics providers so that
curly braces and shortcodes inside code block bodies are no longer
treated as Pandoc attributes or Quarto shortcodes.
Fence headers (e.g. \`\`\`{r}) remain outside the exclusion range so
completions continue to work there.
@mcanouil mcanouil self-assigned this Feb 22, 2026
@mcanouil mcanouil added the Type: Bug 🐛 Issues related to bugs, errors, or mistakes label Feb 22, 2026
…kage

Move getErrorMessage to the schema package's own errors module instead of
importing it from @quarto-wizard/core, which was not declared as a
dependency in package.json and caused Vite resolution failures in CI.
…ppets packages

Move getErrorMessage to each package's own errors module for
schema-cache.ts, snippet-cache.ts, and snippets.ts, mirroring the
earlier fix for schema.ts.
- Extract generic AsyncKeyedCache<T> to deduplicate installed extensions
  cache and workspace schema index (eliminates ~90 lines).
- Replace positional optional parameters with options objects
  (InstallOptions, UseOptions, BrandOptions) across quarto.ts and all
  call sites.
- Add validationVersion tracking to YamlDiagnosticsProvider to prevent
  stale async results from overwriting fresh diagnostics.
- Remove debouncedValidate.cancel() from onDidCloseTextDocument handlers
  to avoid cancelling pending validations for other documents.
- Fix isRebuilding guard in ask.ts to use queueMicrotask, matching the
  rebuildItems pattern and preventing async event interleaving.
- Switch elementAttribute and shortcode providers to cached extension
  discovery for consistent performance.
- Compile closing fence regex once per code block instead of every line.
- Separate internal error details from user-facing network messages.
- Store and dispose cancellation token listener in install command.
- Inline ensureWorkspaceFolder and extract resolveWorkspacePath helper.
- Restore message: undefined for valid ValidationResult entries.
- Document proxy bypass limitation in network.ts.
- Update CHANGELOG with diagnostics bug fixes.
@mcanouil mcanouil merged commit 979bb35 into main Feb 22, 2026
7 checks passed
@mcanouil mcanouil deleted the fix/security-hardening-and-provider-robustness branch February 22, 2026 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Bug 🐛 Issues related to bugs, errors, or mistakes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant