github/copilot-sdk · error

unsafe runtime asset path

Error message

unsafe runtime asset path %q

What it means

For each regular file in the tar, installRuntimeAssets cleans the entry name and requires filepath.IsLocal(clean) before writing. Non-local names (absolute paths, '..' escapes) are rejected with "unsafe runtime asset path" to block path traversal / zip-slip extraction outside installDir.

Solutions

  1. Do not disable this check — the archive is untrusted or malformed
  2. Repackage the runtime archive so all member names are relative and within the archive root
  3. Inspect the archive (tar -tvf) to find the offending member and fix the packaging script
  4. Only bypass via a vetted, trusted archive after security review

Example fix

// before
// packaging script:
// tar -czf runtime.tar.gz /abs/path/assets   // absolute members -> rejected
// after
// (cd /abs/path && tar -czf runtime.tar.gz assets) // relative members only
Defensive patterns

Strategy: validation

Validate before calling

archive, _ := io.ReadAll(src)
_ = iterateTarHeaders(archive, func(name string) error {
    clean := filepath.Clean(filepath.FromSlash(name))
    if !filepath.IsLocal(clean) {
        return fmt.Errorf("archive contains unsafe member %q", name)
    }
    return nil
})

Type guard

func safeArchiveMember(name string) bool {
    return filepath.IsLocal(filepath.Clean(filepath.FromSlash(name)))
}

Try / catch

err := embeddedcli.InstallRuntime(ctx, cfg, dir)
if err != nil && strings.Contains(err.Error(), "unsafe runtime asset path") {
    return fmt.Errorf("refusing to install: runtime archive contains path-traversal members; repackage it")
}

Prevention

When it happens

Trigger: A tar entry header.Name is absolute ("/etc/passwd"), contains ".." ("../../x"), or otherwise resolves outside the install directory after filepath.Clean.

Common situations: A maliciously crafted or wrongly packaged runtime archive containing absolute or parent-relative paths; archives built on systems embedding absolute member names.

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


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/47d91c4279da90e4. Report an issue: GitHub.

Appendix: source

Thrown at go/internal/embeddedcli/embeddedcli.go:397

	if err != nil {
		return fmt.Errorf("opening runtime assets: %w", err)
	}
	defer gzipReader.Close()
	tarReader := tar.NewReader(gzipReader)
	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return fmt.Errorf("reading runtime assets: %w", err)
		}
		if header.Typeflag != tar.TypeReg {
			continue
		}
		clean := filepath.Clean(filepath.FromSlash(header.Name))
		if !filepath.IsLocal(clean) {
			return fmt.Errorf("unsafe runtime asset path %q", header.Name)
		}
		content, err := io.ReadAll(tarReader)
		if err != nil {
			return fmt.Errorf("reading runtime asset %q: %w", header.Name, err)
		}
		path := filepath.Join(installDir, clean)
		if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
			return fmt.Errorf("creating runtime asset directory: %w", err)
		}
		hash := sha256.Sum256(content)
		mode := os.FileMode(header.Mode & 0777)
		if err := installVerifiedFile(path, bytes.NewReader(content), hash[:], mode, "runtime asset"); err != nil {
			return err
		}
	}
	runtimeAssetsInstalled = true
	return nil
}

View on GitHub (pinned to cd8cf15dc3)