microsoft/aspire · error · InvalidDataException
Aspire skills archive entry
Error message
Aspire skills archive entry '{0}' is not safe. What it means
GetSafeArchiveDestinationPath validates each archive entry name before writing it to disk. Rooted names, empty names, or segments failing the portability check (including '..') are rejected with this InvalidDataException, preventing zip-slip style attacks during extraction.
Solutions
- Repackage the archive so every entry is a clean relative path under a single root folder
- Strip absolute prefixes and '..' segments from entry names before archiving
- Re-download the official archive; do not extract untrusted third-party versions
Example fix
// before (tar entry) ../../../home/user/.bashrc // after cskills/my-skill/SKILL.md
Defensive patterns
Strategy: validation
Validate before calling
var name = entry.Name.Replace('\\', '/');
var segs = name.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (Path.IsPathRooted(name) || segs.Length == 0 || segs.Contains(".."))
throw new Exception($"Unsafe archive entry '{entry.Name}'."); Try / catch
try { await provider.CreateAsync(...); }
catch (InvalidDataException ex) when (ex.Message.Contains("entry") && ex.Message.Contains("is not safe")) { /* reject/repackage the archive */ } Prevention
- Only extract archives from trusted sources
- Pre-scan archives with 'tar -tf' or zip listing for '..' or absolute entries
- Package entries under a single root folder
When it happens
Trigger: ExtractArchive extracts an archive entry whose name is absolute ('/x'), empty, or contains non-portable segments ('../secrets', reserved device names), causing GetSafeArchiveDestinationPath to throw.
Common situations: Malicious or corrupted archives with traversal entries; archives built on Windows with backslash-absolute or drive-letter paths; tooling that emits entries like './..' or '//'.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Aspire skills archive entry
- Symlink ' ' targets ' ' which resolves outside the…
- Tar entry ' ' would extract outside the destination…
- Aspire skills archive entry
- Aspire skills bundle path
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/1a215cb945844b23.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Agents/AspireSkills/AspireSkillsBundleProvider.cs:562
var destinationFileDirectory = Path.GetDirectoryName(destinationPath);
if (!string.IsNullOrEmpty(destinationFileDirectory))
{
Directory.CreateDirectory(destinationFileDirectory);
}
entry.ExtractToFile(destinationPath, overwrite: false);
}
}
private static string GetSafeArchiveDestinationPath(string destinationRoot, string entryName)
{
var normalizedEntryName = entryName.Replace('\\', '/');
var segments = normalizedEntryName.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (Path.IsPathRooted(normalizedEntryName) ||
segments.Length == 0 ||
segments.Any(static segment => !IsPortablePathSegment(segment)))
{
throw new InvalidDataException(string.Format(CultureInfo.InvariantCulture, "Aspire skills archive entry '{0}' is not safe.", entryName));
}
var destinationPath = Path.GetFullPath(Path.Combine(destinationRoot, normalizedEntryName.Replace('/', Path.DirectorySeparatorChar)));
if (!destinationPath.StartsWith(destinationRoot + Path.DirectorySeparatorChar, StringComparison.Ordinal) &&
!string.Equals(destinationPath, destinationRoot, StringComparison.Ordinal))
{
throw new InvalidDataException(string.Format(CultureInfo.InvariantCulture, "Aspire skills archive entry '{0}' escapes the extraction directory.", entryName));
}
return destinationPath;
}
private static DirectoryInfo FindBundleRoot(string extractionDirectory)
{
var rootManifestPath = Path.Combine(extractionDirectory, ManifestFileName);
if (File.Exists(rootManifestPath))
{
return new DirectoryInfo(extractionDirectory);View on GitHub (pinned to 25830f84bd)