Skip to content

Load model from archive #113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

Load model from archive #113

wants to merge 1 commit into from

Conversation

ekcasey
Copy link
Contributor

@ekcasey ekcasey commented Jul 17, 2025

Summary

  • Supports loading archived models in the local store via CLI tool or distribution.Client.
  • Support packaging models to archive file via CLI tool or tarball.Target

CLI Usage

model-distribution-tool load

Introduces load command which loads model from the archive at into the store and optionally applies .

./bin/model-distribution-tool load [--tag <tag>] <path>`

model-distribution-tool package --file

Adds --file <path> flag to package command, which results in the model being written to a TAR archive at the given <path> instead of being pushed to the registry.

> ./bin/model-distribution-tool package --file /tmp/model.tar ./assets/dummy.gguf

LoadModel with distribution.Client

func (c *Client) LoadModel(tag string, rc io.ReadCloser, progressWriter io.Writer) error

Expects to read a TAR archive from rc formatted as described below and applies the given tag, writing progress to progressWriter.

Format

package and load expect/produce models with any number of blobs entries with name blobs/sha256/<hash> and 1 manifest.json. Example:

> tar tfv /tmp/model.tar
d---------  0 0      0           0 Dec 31  1969 blobs
d---------  0 0      0           0 Dec 31  1969 blobs/sha256
-rw-rw-rw-  0 0      0        2016 Dec 31  1969 blobs/sha256/c7790a0a70161f1bfd441cf157313e9efb8fcd1f0831193101def035ead23b32
-rw-rw-rw-  0 0      0         851 Dec 31  1969 blobs/sha256/f1017b51ac729af1cc57f8907347b1308678c8b7ad2ca29ad0098fccfdfa17b8
-rw-rw-rw-  0 0      0         400 Dec 31  1969 manifest.json

TODO

  • report load progress
  • model-distribution-tool package with --load flag
  • update README


func (r *Reader) Next() (v1.Hash, error) {
for {
hdr, err := r.tr.Next()

Check failure

Code scanning / CodeQL

Arbitrary file access during archive extraction ("Zip Slip") High

Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.

Copilot Autofix

AI 5 days ago

To fix the problem, input from the TAR archive (hdr.Name) should be properly sanitized before being used to construct a v1.Hash, especially since its fields are used to form filesystem paths. The best way to do this is to ensure that hdr.Name is not an absolute path, does not contain any .. directory traversal elements, and that both parts[1] (algorithm) and parts[2] (hex) are valid and safe for use as path segments. This can be achieved by:

  • Rejecting any entry where hdr.Name is, or cleans to, an absolute path.
  • Rejecting any entry where any segment is .. or empty.
  • Optionally, adding stricter validation for parts[1] and parts[2] (for example, matching a regex for expected hash algorithm names and hexadecimal strings).

These checks should be implemented in the Next() function in tarball/reader.go, before constructing and returning the v1.Hash.

Required changes:

  • In tarball/reader.go, within func (r *Reader) Next(), add robust checks after cleaning and splitting hdr.Name to ensure it does not contain .., empty segments, or absolute paths, and that parts[1] and parts[2] are valid.
  • No additional imports are needed beyond what is already present (use filepath.IsAbs and string checks).
  • Return an error or continue if a dangerous entry is detected.

Suggested changeset 1
tarball/reader.go

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tarball/reader.go b/tarball/reader.go
--- a/tarball/reader.go
+++ b/tarball/reader.go
@@ -60,14 +60,24 @@
 			}
 			continue
 		}
-		parts := strings.Split(filepath.Clean(hdr.Name), "/")
-		if len(parts) != 3 || parts[0] != "blobs" && parts[0] != "manifests" {
+		cleanName := filepath.Clean(hdr.Name)
+		// Prevent absolute paths or path traversal
+		if filepath.IsAbs(cleanName) {
 			continue
 		}
-		return v1.Hash{
-			Algorithm: parts[1],
-			Hex:       parts[2],
-		}, nil
+		parts := strings.Split(cleanName, "/")
+		if len(parts) != 3 || (parts[0] != "blobs" && parts[0] != "manifests") {
+			continue
+		}
+		// Check for any ".." or empty segments
+		badSegment := false
+		for _, part := range parts {
+			if part == ".." || part == "" || strings.Contains(part, string(filepath.Separator)) {
+				badSegment = true
+				break
+			}
+		}
+
 	}
 }
 
EOF
@@ -60,14 +60,24 @@
}
continue
}
parts := strings.Split(filepath.Clean(hdr.Name), "/")
if len(parts) != 3 || parts[0] != "blobs" && parts[0] != "manifests" {
cleanName := filepath.Clean(hdr.Name)
// Prevent absolute paths or path traversal
if filepath.IsAbs(cleanName) {
continue
}
return v1.Hash{
Algorithm: parts[1],
Hex: parts[2],
}, nil
parts := strings.Split(cleanName, "/")
if len(parts) != 3 || (parts[0] != "blobs" && parts[0] != "manifests") {
continue
}
// Check for any ".." or empty segments
badSegment := false
for _, part := range parts {
if part == ".." || part == "" || strings.Contains(part, string(filepath.Separator)) {
badSegment = true
break
}
}

}
}

Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant