Skip to content

Commit b56b267

Browse files
fix(go): harden native SDK publishing and install
1 parent 9a98492 commit b56b267

10 files changed

Lines changed: 100 additions & 24 deletions

File tree

.github/workflows/publish-go-sdk.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ jobs:
2929
with:
3030
go-version: '1.22.2'
3131

32+
- name: Configure Git author
33+
run: |
34+
git config user.name 'github-actions[bot]'
35+
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
36+
3237
- name: Update bindings native version metadata
3338
shell: bash
3439
run: |
@@ -43,6 +48,11 @@ jobs:
4348
// NativeLibReleaseTag is the GitHub release tag used to fetch native artifacts.
4449
const NativeLibReleaseTag = "${C_SDK_VERSION}"
4550
EOF
51+
cat > sdks/go/tools/install/release.go <<EOF
52+
package main
53+
54+
const defaultReleaseTag = "${C_SDK_VERSION}"
55+
EOF
4656
4757
- name: Publish source-only module tags
4858
run: ./sdks/go/scripts/publish-sdk-module-tags.sh "${{ github.event.inputs.go_version }}"

examples/go/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Install the native library for your platform:
2323
../../sdks/go/scripts/link_dev_lib.sh c-sdk-v0.9.0
2424

2525
# Or after go get:
26-
go run github.com/usemoss/moss/sdks/go/tools/install@latest
26+
go run github.com/usemoss/moss/sdks/go/tools/install@latest --vendor
2727
```
2828

2929
Then run an example:

sdks/go/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ The Go SDK follows the same two-layer design as the other Moss SDKs:
99

1010
```bash
1111
go get github.com/usemoss/moss/sdks/go/sdk
12-
go run github.com/usemoss/moss/sdks/go/tools/install@latest
12+
go run github.com/usemoss/moss/sdks/go/tools/install@latest --vendor
1313
```
1414

1515
The install tool downloads the static `libmoss` library for your platform from
1616
[Moss C SDK GitHub Releases](https://github.com/usemoss/moss/releases). You need
1717
CGO and a C compiler, but not a manual C SDK download or `LD_LIBRARY_PATH`.
18-
For external projects, the installer vendors the bindings before placing the
19-
library next to them, so the normal `go build` command finds it.
18+
For external projects, the explicit `--vendor` option lets the installer vendor
19+
the bindings before placing the library next to them, so the normal `go build`
20+
command finds it.
2021

2122
From a checkout of the bindings package, you can instead use:
2223

sdks/go/bindings/README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,19 @@ It mirrors the role of the other language bindings packages in this repository:
1313

1414
```bash
1515
go get github.com/usemoss/moss/sdks/go/sdk
16-
go run github.com/usemoss/moss/sdks/go/tools/install@latest
16+
go run github.com/usemoss/moss/sdks/go/tools/install@latest --vendor
1717
```
1818

1919
The install tool downloads a prebuilt static library for your platform from
2020
[C SDK GitHub Releases](https://github.com/usemoss/moss/releases). No manual C
21-
SDK download, `LD_LIBRARY_PATH`, or `-tags libmoss` is required. For a
22-
downloaded module, it runs `go mod vendor` and installs the library beside the
23-
vendored bindings; subsequent `go build` commands use that copy automatically.
21+
SDK download, `LD_LIBRARY_PATH`, or `-tags libmoss` is required. The explicit
22+
`--vendor` option permits the installer to run `go mod vendor` and install the
23+
library beside the vendored bindings; subsequent `go build` commands use that
24+
copy automatically. If your project already vendors Moss, omit `--vendor` to
25+
avoid rewriting its `vendor/` tree.
26+
27+
Run the installer after your application imports the Moss SDK, so `go mod
28+
vendor` includes the bindings package.
2429

2530
Requirements:
2631

sdks/go/bindings/prebuilt_windows_amd64.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ package mosscore
44

55
/*
66
#cgo CFLAGS: -I${SRCDIR}/include
7-
#cgo LDFLAGS: -L${SRCDIR}/lib/windows-amd64 moss.lib
7+
#cgo LDFLAGS: ${SRCDIR}/lib/windows-amd64/moss.lib
88
*/
99
import "C"

sdks/go/scripts/publish-sdk-module-tags.sh

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,10 @@ git add sdks/go/bindings/go.mod sdks/go/sdk/go.mod sdks/go/bindings/version.go
2424
git add sdks/go/bindings/include/libmoss.h sdks/go/bindings/generate.go
2525
git add sdks/go/tools/install
2626

27-
if git diff --cached --quiet; then
28-
echo "Nothing to publish for bindings/sdk ${VERSION}" >&2
29-
exit 1
27+
if ! git diff --cached --quiet; then
28+
git commit -m "chore(go): publish bindings and sdk ${VERSION}"
3029
fi
3130

32-
git commit -m "chore(go): publish bindings and sdk ${VERSION}"
33-
3431
for tag in \
3532
"sdks/go/bindings/${VERSION}" \
3633
"sdks/go/sdk/${VERSION}" \

sdks/go/sdk/README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ The Go SDK now has two layers:
2727

2828
```bash
2929
go get github.com/usemoss/moss/sdks/go/sdk
30-
go run github.com/usemoss/moss/sdks/go/tools/install@latest
30+
go run github.com/usemoss/moss/sdks/go/tools/install@latest --vendor
3131
```
3232

3333
The install tool downloads the static `libmoss` library for your platform. See
3434
[`../bindings/README.md`](../bindings/README.md) for Windows toolchain notes.
35-
It vendors the SDK so CGO can link the downloaded library from a writable
36-
directory; commit `vendor/` if your project commits vendored dependencies.
35+
The explicit `--vendor` option vendors the SDK so CGO can link the downloaded
36+
library from a writable directory; commit `vendor/` if your project commits
37+
vendored dependencies. Run it after your application imports the SDK so the
38+
bindings package is included in `vendor/`.
3739

3840
Monorepo development uses the workspace in [`../go.work`](../go.work) and
3941
[`../scripts/link_dev_lib.sh`](../scripts/link_dev_lib.sh).

sdks/go/tools/install/main.go

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import (
1515
"net/http"
1616
"os"
1717
"os/exec"
18+
"path"
1819
"path/filepath"
1920
"runtime"
2021
"strings"
2122
)
2223

2324
const (
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

3736
var 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+
330363
func 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 {

sdks/go/tools/install/main_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package main
2+
3+
import (
4+
"path/filepath"
5+
"testing"
6+
)
7+
8+
func TestSafeArchiveTarget(t *testing.T) {
9+
root := t.TempDir()
10+
11+
target, err := safeArchiveTarget(root, "include/libmoss.h")
12+
if err != nil {
13+
t.Fatalf("safeArchiveTarget returned an error: %v", err)
14+
}
15+
want := filepath.Join(root, "include", "libmoss.h")
16+
if target != want {
17+
t.Fatalf("safeArchiveTarget() = %q, want %q", target, want)
18+
}
19+
20+
for _, input := range []string{"..", "../outside", "/outside"} {
21+
if _, err := safeArchiveTarget(root, input); err == nil {
22+
t.Errorf("safeArchiveTarget(%q) succeeded, want error", input)
23+
}
24+
}
25+
}

sdks/go/tools/install/release.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package main
2+
3+
const defaultReleaseTag = "c-sdk-v0.9.0"

0 commit comments

Comments
 (0)