@@ -15,13 +15,13 @@ import (
1515 "net/http"
1616 "os"
1717 "os/exec"
18+ "path"
1819 "path/filepath"
1920 "runtime"
2021 "strings"
2122)
2223
2324const (
24- defaultReleaseTag = "c-sdk-v0.9.0"
2525 defaultRepo = "usemoss/moss"
2626 bindingsModulePath = "github.com/usemoss/moss/sdks/go/bindings"
2727)
@@ -31,7 +31,6 @@ type platform struct {
3131 triple string
3232 libFile string
3333 srcLib string
34- ldflags string
3534}
3635
3736var platforms = []platform {
@@ -58,10 +57,11 @@ func main() {
5857 releaseTag := flag .String ("release" , defaultReleaseTag , "C SDK GitHub release tag" )
5958 repo := flag .String ("repo" , defaultRepo , "GitHub repository (owner/name)" )
6059 bindingsDir := flag .String ("bindings" , "" , "bindings directory (default: auto-detect)" )
60+ vendor := flag .Bool ("vendor" , false , "run go mod vendor before installing into a downloaded module" )
6161 force := flag .Bool ("force" , false , "re-download even if the library is already installed" )
6262 flag .Parse ()
6363
64- root , err := resolveWritableBindingsDir (* bindingsDir )
64+ root , err := resolveWritableBindingsDir (* bindingsDir , * vendor )
6565 if err != nil {
6666 fatal (err )
6767 }
@@ -92,9 +92,9 @@ func main() {
9292}
9393
9494// resolveWritableBindingsDir returns the bindings package CGO will compile.
95- // Downloaded Go modules are read-only, so an external consumer is vendored
96- // before installing the native library beside the bindings source .
97- func resolveWritableBindingsDir (explicit string ) (string , error ) {
95+ // Downloaded Go modules are read-only, so consumers either need existing
96+ // vendored bindings or must explicitly permit the installer to create them .
97+ func resolveWritableBindingsDir (explicit string , allowVendor bool ) (string , error ) {
9898 root , err := resolveBindingsDir (explicit )
9999 if err != nil {
100100 return "" , err
@@ -105,6 +105,12 @@ func resolveWritableBindingsDir(explicit string) (string, error) {
105105 if explicit != "" || strings .TrimSpace (os .Getenv ("MOSS_BINDINGS_DIR" )) != "" {
106106 return "" , fmt .Errorf ("bindings directory %s is not writable" , root )
107107 }
108+ if vendoredRoot , err := bindingsDirFromGoList ("-mod=vendor" ); err == nil && isWritableDir (vendoredRoot ) {
109+ return vendoredRoot , nil
110+ }
111+ if ! allowVendor {
112+ return "" , errors .New ("the downloaded bindings module is read-only; run `go mod vendor` first, or rerun moss install with --vendor to allow it to regenerate vendor/" )
113+ }
108114
109115 goMod , err := goEnv ("GOMOD" )
110116 if err != nil || goMod == os .DevNull || goMod == "" {
@@ -317,7 +323,10 @@ func extractTarGz(archivePath, destRoot, version, triple string) error {
317323 continue
318324 }
319325 rel := strings .TrimPrefix (hdr .Name , prefix )
320- target := filepath .Join (destRoot , rel )
326+ target , err := safeArchiveTarget (destRoot , rel )
327+ if err != nil {
328+ return fmt .Errorf ("invalid archive path %q: %w" , hdr .Name , err )
329+ }
321330 if err := os .MkdirAll (filepath .Dir (target ), 0o755 ); err != nil {
322331 return err
323332 }
@@ -327,6 +336,30 @@ func extractTarGz(archivePath, destRoot, version, triple string) error {
327336 }
328337}
329338
339+ func safeArchiveTarget (destRoot , rel string ) (string , error ) {
340+ clean := path .Clean (rel )
341+ if clean == "." || clean == ".." || strings .HasPrefix (clean , "../" ) || path .IsAbs (clean ) {
342+ return "" , errors .New ("path escapes extraction root" )
343+ }
344+
345+ root , err := filepath .Abs (destRoot )
346+ if err != nil {
347+ return "" , err
348+ }
349+ target , err := filepath .Abs (filepath .Join (root , filepath .FromSlash (clean )))
350+ if err != nil {
351+ return "" , err
352+ }
353+ relative , err := filepath .Rel (root , target )
354+ if err != nil {
355+ return "" , err
356+ }
357+ if relative == ".." || strings .HasPrefix (relative , ".." + string (filepath .Separator )) {
358+ return "" , errors .New ("path escapes extraction root" )
359+ }
360+ return target , nil
361+ }
362+
330363func writeFile (path string , r io.Reader , mode int64 ) error {
331364 out , err := os .OpenFile (path , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , fileMode (mode ))
332365 if err != nil {
0 commit comments