diff --git a/.github/workflows/build-and-test-v3.yml b/.github/workflows/build-and-test-v3.yml index 61cb9dde9..6ae09eab8 100644 --- a/.github/workflows/build-and-test-v3.yml +++ b/.github/workflows/build-and-test-v3.yml @@ -173,16 +173,23 @@ jobs: cd ${{ matrix.template }} wails3 build - results: - if: ${{ always() }} - runs-on: ubuntu-latest - name: v3 Build Results - needs: [test_go, test_js, test_templates] - steps: - - run: | - result="${{ needs.build.result }}" - if [[ $result == "success" || $result == "skipped" ]]; then - exit 0 - else - exit 1 - fi \ No newline at end of file +results: + if: ${{ always() }} + runs-on: ubuntu-latest + name: v3 Build Results + needs: [test_go, test_js, test_templates] + steps: + - run: | + go_result="${{ needs.test_go.result }}" + js_result="${{ needs.test_js.result }}" + templates_result="${{ needs.test_templates.result }}" + + if [[ $go_result == "success" || $go_result == "skipped" ]] && \ + [[ $js_result == "success" || $js_result == "skipped" ]] && \ + [[ $templates_result == "success" || $templates_result == "skipped" ]]; then + echo "All required jobs succeeded or were skipped" + exit 0 + else + echo "One or more required jobs failed" + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/pr.yml b/.github/workflows/pr-v2.yml similarity index 89% rename from .github/workflows/pr.yml rename to .github/workflows/pr-v2.yml index 52586661c..572260b8b 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr-v2.yml @@ -11,13 +11,13 @@ on: jobs: check_docs: name: Check Docs - if: ${{github.repository == 'wailsapp/wails' && contains(github.head_ref,'feature/')}} + if: ${{github.repository == 'wailsapp/wails' && github.base_ref == 'master'}} runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Verify Changed files - uses: tj-actions/verify-changed-files@v17 + uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1 id: verify-changed-files with: files: | @@ -55,7 +55,7 @@ jobs: test_go: name: Run Go Tests runs-on: ${{ matrix.os }} - if: github.event.review.state == 'approved' + if: github.event.review.state == 'approved' && github.repository == 'wailsapp/wails' && github.base_ref == 'master' strategy: matrix: os: [ubuntu-22.04, windows-latest, macos-latest, ubuntu-24.04] diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 453e4cb85..7533a3a04 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -5,6 +5,7 @@ on: branches: - main - master + - v3-alpha paths: - .github/workflows/semgrep.yml schedule: diff --git a/.github/workflows/upload-source-documents.yml b/.github/workflows/upload-source-documents.yml index df15246fc..69d6c3e48 100644 --- a/.github/workflows/upload-source-documents.yml +++ b/.github/workflows/upload-source-documents.yml @@ -15,7 +15,7 @@ jobs: - name: Verify Changed files id: changed-files - uses: tj-actions/changed-files@v41 + uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1 with: files: | website/**/*.mdx diff --git a/v2/cmd/wails/build.go b/v2/cmd/wails/build.go index 7364df8ba..05f235078 100644 --- a/v2/cmd/wails/build.go +++ b/v2/cmd/wails/build.go @@ -85,6 +85,7 @@ func buildApplication(f *flags.Build) error { GarbleArgs: f.GarbleArgs, SkipBindings: f.SkipBindings, ProjectData: projectOptions, + SkipEmbedCreate: f.SkipEmbedCreate, } tableData := pterm.TableData{ diff --git a/v2/cmd/wails/doctor.go b/v2/cmd/wails/doctor.go index 5306cab17..eac5048b3 100644 --- a/v2/cmd/wails/doctor.go +++ b/v2/cmd/wails/doctor.go @@ -2,12 +2,13 @@ package main import ( "fmt" - "github.com/wailsapp/wails/v2/internal/shell" "runtime" "runtime/debug" "strconv" "strings" + "github.com/wailsapp/wails/v2/internal/shell" + "github.com/pterm/pterm" "github.com/jaypipes/ghw" @@ -78,6 +79,7 @@ func diagnoseEnvironment(f *flags.Doctor) error { {pterm.Bold.Sprint("OS"), info.OS.Name}, {pterm.Bold.Sprint("Version"), info.OS.Version}, {pterm.Bold.Sprint("ID"), info.OS.ID}, + {pterm.Bold.Sprint("Branding"), info.OS.Branding}, {pterm.Bold.Sprint("Go Version"), runtime.Version()}, {pterm.Bold.Sprint("Platform"), runtime.GOOS}, {pterm.Bold.Sprint("Architecture"), runtime.GOARCH}, diff --git a/v2/cmd/wails/flags/buildcommon.go b/v2/cmd/wails/flags/buildcommon.go index 4bbc4c62c..a22f7a502 100644 --- a/v2/cmd/wails/flags/buildcommon.go +++ b/v2/cmd/wails/flags/buildcommon.go @@ -1,15 +1,16 @@ package flags type BuildCommon struct { - LdFlags string `description:"Additional ldflags to pass to the compiler"` - Compiler string `description:"Use a different go compiler to build, eg go1.15beta1"` - SkipBindings bool `description:"Skips generation of bindings"` - RaceDetector bool `name:"race" description:"Build with Go's race detector"` - SkipFrontend bool `name:"s" description:"Skips building the frontend"` - Verbosity int `name:"v" description:"Verbosity level (0 = quiet, 1 = normal, 2 = verbose)"` - Tags string `description:"Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated"` - NoSyncGoMod bool `description:"Don't sync go.mod"` - SkipModTidy bool `name:"m" description:"Skip mod tidy before compile"` + LdFlags string `description:"Additional ldflags to pass to the compiler"` + Compiler string `description:"Use a different go compiler to build, eg go1.15beta1"` + SkipBindings bool `description:"Skips generation of bindings"` + RaceDetector bool `name:"race" description:"Build with Go's race detector"` + SkipFrontend bool `name:"s" description:"Skips building the frontend"` + Verbosity int `name:"v" description:"Verbosity level (0 = quiet, 1 = normal, 2 = verbose)"` + Tags string `description:"Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated"` + NoSyncGoMod bool `description:"Don't sync go.mod"` + SkipModTidy bool `name:"m" description:"Skip mod tidy before compile"` + SkipEmbedCreate bool `description:"Skips creation of embed files"` } func (c BuildCommon) Default() BuildCommon { diff --git a/v2/cmd/wails/flags/dev.go b/v2/cmd/wails/flags/dev.go index 501450a98..7673cad05 100644 --- a/v2/cmd/wails/flags/dev.go +++ b/v2/cmd/wails/flags/dev.go @@ -117,21 +117,23 @@ func (d *Dev) loadAndMergeProjectConfig() error { // GenerateBuildOptions creates a build.Options using the flags func (d *Dev) GenerateBuildOptions() *build.Options { result := &build.Options{ - OutputType: "dev", - Mode: build.Dev, - Devtools: true, - Arch: runtime.GOARCH, - Pack: true, - Platform: runtime.GOOS, - LDFlags: d.LdFlags, - Compiler: d.Compiler, - ForceBuild: d.ForceBuild, - IgnoreFrontend: d.SkipFrontend, - SkipBindings: d.SkipBindings, - Verbosity: d.Verbosity, - WailsJSDir: d.WailsJSDir, - RaceDetector: d.RaceDetector, - ProjectData: d.projectConfig, + OutputType: "dev", + Mode: build.Dev, + Devtools: true, + Arch: runtime.GOARCH, + Pack: true, + Platform: runtime.GOOS, + LDFlags: d.LdFlags, + Compiler: d.Compiler, + ForceBuild: d.ForceBuild, + IgnoreFrontend: d.SkipFrontend, + SkipBindings: d.SkipBindings, + SkipModTidy: d.SkipModTidy, + Verbosity: d.Verbosity, + WailsJSDir: d.WailsJSDir, + RaceDetector: d.RaceDetector, + ProjectData: d.projectConfig, + SkipEmbedCreate: d.SkipEmbedCreate, } return result diff --git a/v2/go.mod b/v2/go.mod index 036dc4812..7bf6de3cd 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -15,6 +15,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.6.0 + github.com/gorilla/websocket v1.5.3 github.com/jackmordaunt/icns v1.0.0 github.com/jaypipes/ghw v0.13.0 github.com/labstack/echo/v4 v4.13.3 diff --git a/v2/go.sum b/v2/go.sum index ccf53eb07..921ea0477 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -107,6 +107,8 @@ github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU= diff --git a/v2/internal/app/app_dev.go b/v2/internal/app/app_dev.go index 89265c9b9..018cb226c 100644 --- a/v2/internal/app/app_dev.go +++ b/v2/internal/app/app_dev.go @@ -46,7 +46,12 @@ func CreateApp(appoptions *options.App) (*App, error) { ctx = context.WithValue(ctx, "debug", true) ctx = context.WithValue(ctx, "devtoolsEnabled", true) - // Set up logger + // Set up logger if the appoptions.LogLevel is an invalid value, set it to the default log level + appoptions.LogLevel, err = pkglogger.StringToLogLevel(appoptions.LogLevel.String()) + if err != nil { + return nil, err + } + myLogger := logger.New(appoptions.Logger) myLogger.SetLogLevel(appoptions.LogLevel) @@ -91,15 +96,8 @@ func CreateApp(appoptions *options.App) (*App, error) { if frontendDevServerURLFlag != nil { frontendDevServerURL = *frontendDevServerURLFlag } - // Only override LogLevel if the flag was explicitly set - if loglevelFlag != nil && devFlags.Lookup("loglevel").Value.String() != appoptions.LogLevel.String() { - loggerLevel, err := pkglogger.StringToLogLevel(*loglevelFlag) - if err != nil { - return nil, err - } - if loggerLevel != appoptions.LogLevel { - myLogger.SetLogLevel(loggerLevel) - } + if loglevelFlag != nil { + loglevel = *loglevelFlag } } @@ -176,6 +174,17 @@ func CreateApp(appoptions *options.App) (*App, error) { ctx = context.WithValue(ctx, "devserver", devServer) } + if loglevel != "" { + level, err := pkglogger.StringToLogLevel(loglevel) + if err != nil { + return nil, err + } + // Only set the log level if it's different from the appoptions.LogLevel + if level != appoptions.LogLevel { + myLogger.SetLogLevel(level) + } + } + // Attach logger to context ctx = context.WithValue(ctx, "logger", myLogger) ctx = context.WithValue(ctx, "buildtype", "dev") @@ -212,7 +221,7 @@ func CreateApp(appoptions *options.App) (*App, error) { eventHandler := runtime.NewEvents(myLogger) ctx = context.WithValue(ctx, "events", eventHandler) - messageDispatcher := dispatcher.NewDispatcher(ctx, myLogger, appBindings, eventHandler, appoptions.ErrorFormatter) + messageDispatcher := dispatcher.NewDispatcher(ctx, myLogger, appBindings, eventHandler, appoptions.ErrorFormatter, appoptions.DisablePanicRecovery) // Create the frontends and register to event handler desktopFrontend := desktop.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher) diff --git a/v2/internal/app/app_production.go b/v2/internal/app/app_production.go index 4c217b17c..9eb0e5a66 100644 --- a/v2/internal/app/app_production.go +++ b/v2/internal/app/app_production.go @@ -82,7 +82,7 @@ func CreateApp(appoptions *options.App) (*App, error) { ctx = context.WithValue(ctx, "buildtype", "production") } - messageDispatcher := dispatcher.NewDispatcher(ctx, myLogger, appBindings, eventHandler, appoptions.ErrorFormatter) + messageDispatcher := dispatcher.NewDispatcher(ctx, myLogger, appBindings, eventHandler, appoptions.ErrorFormatter, appoptions.DisablePanicRecovery) appFrontend := desktop.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher) eventHandler.AddFrontend(appFrontend) diff --git a/v2/internal/binding/binding_test/binding_deepelements_test.go b/v2/internal/binding/binding_test/binding_deepelements_test.go index 488c58f6d..034687474 100644 --- a/v2/internal/binding/binding_test/binding_deepelements_test.go +++ b/v2/internal/binding/binding_test/binding_deepelements_test.go @@ -63,18 +63,18 @@ export namespace binding_test { DoubleFour: number[][]; Triple: number[][][]; SingleMap: Record; - SliceMap: Record; - DoubleSliceMap: Record; - ArrayMap: Record; - DoubleArrayMap1: Record; - DoubleArrayMap2: Record; - DoubleArrayMap3: Record; + SliceMap: Record>; + DoubleSliceMap: Record>>; + ArrayMap: Record>; + DoubleArrayMap1: Record>>; + DoubleArrayMap2: Record>>; + DoubleArrayMap3: Record>>; OneStructs: DeepMessage[]; TwoStructs: DeepMessage[][]; ThreeStructs: DeepMessage[][][]; - MapStructs: Record; - MapTwoStructs: Record; - MapThreeStructs: Record; + MapStructs: Record>; + MapTwoStructs: Record>>; + MapThreeStructs: Record>>>; static createFrom(source: any = {}) { return new DeepElements(source); @@ -97,9 +97,9 @@ export namespace binding_test { this.OneStructs = this.convertValues(source["OneStructs"], DeepMessage); this.TwoStructs = this.convertValues(source["TwoStructs"], DeepMessage); this.ThreeStructs = this.convertValues(source["ThreeStructs"], DeepMessage); - this.MapStructs = this.convertValues(source["MapStructs"], DeepMessage[], true); - this.MapTwoStructs = this.convertValues(source["MapTwoStructs"], DeepMessage[][], true); - this.MapThreeStructs = this.convertValues(source["MapThreeStructs"], DeepMessage[][][], true); + this.MapStructs = this.convertValues(source["MapStructs"], Array, true); + this.MapTwoStructs = this.convertValues(source["MapTwoStructs"], Array>, true); + this.MapThreeStructs = this.convertValues(source["MapThreeStructs"], Array>>, true); } convertValues(a: any, classs: any, asMap: boolean = false): any { diff --git a/v2/internal/frontend/desktop/windows/winc/form.go b/v2/internal/frontend/desktop/windows/winc/form.go index 8a42d63f3..c9acf7278 100644 --- a/v2/internal/frontend/desktop/windows/winc/form.go +++ b/v2/internal/frontend/desktop/windows/winc/form.go @@ -145,7 +145,7 @@ func (fm *Form) Restore() { SC_RESTORE, 0, ) - w32.ShowWindow(fm.hwnd, w32.SW_RESTORE) + w32.ShowWindow(fm.hwnd, w32.SW_SHOW) } // Public methods diff --git a/v2/internal/frontend/devserver/devserver.go b/v2/internal/frontend/devserver/devserver.go index 3d623ed6d..8a130890d 100644 --- a/v2/internal/frontend/devserver/devserver.go +++ b/v2/internal/frontend/devserver/devserver.go @@ -20,17 +20,23 @@ import ( "github.com/wailsapp/wails/v2/internal/frontend/runtime" + "github.com/gorilla/websocket" "github.com/labstack/echo/v4" "github.com/wailsapp/wails/v2/internal/binding" "github.com/wailsapp/wails/v2/internal/frontend" "github.com/wailsapp/wails/v2/internal/logger" "github.com/wailsapp/wails/v2/internal/menumanager" "github.com/wailsapp/wails/v2/pkg/options" - "golang.org/x/net/websocket" ) type Screen = frontend.Screen +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { return true }, +} + type DevWebServer struct { server *echo.Echo ctx context.Context @@ -155,51 +161,64 @@ func (d *DevWebServer) handleReloadApp(c echo.Context) error { } func (d *DevWebServer) handleIPCWebSocket(c echo.Context) error { - websocket.Handler(func(c *websocket.Conn) { - d.LogDebug(fmt.Sprintf("Websocket client %p connected", c)) + conn, err := upgrader.Upgrade(c.Response(), c.Request(), nil) + if err != nil { + d.logger.Error("WebSocket upgrade failed %v", err) + return err + } + d.LogDebug(fmt.Sprintf("WebSocket client %p connected", conn)) + + d.socketMutex.Lock() + d.websocketClients[conn] = &sync.Mutex{} + locker := d.websocketClients[conn] + d.socketMutex.Unlock() + + var wg sync.WaitGroup + + defer func() { + wg.Wait() d.socketMutex.Lock() - d.websocketClients[c] = &sync.Mutex{} - locker := d.websocketClients[c] + delete(d.websocketClients, conn) d.socketMutex.Unlock() + d.LogDebug(fmt.Sprintf("WebSocket client %p disconnected", conn)) + conn.Close() + }() - defer func() { - d.socketMutex.Lock() - delete(d.websocketClients, c) - d.socketMutex.Unlock() - d.LogDebug(fmt.Sprintf("Websocket client %p disconnected", c)) - }() + for { + _, msgBytes, err := conn.ReadMessage() + if err != nil { + break + } - var msg string - defer c.Close() - for { - if err := websocket.Message.Receive(c, &msg); err != nil { - break - } - // We do not support drag in browsers - if msg == "drag" { - continue + msg := string(msgBytes) + wg.Add(1) + + go func(m string) { + defer wg.Done() + + if m == "drag" { + return } - // Notify the other browsers of "EventEmit" - if len(msg) > 2 && strings.HasPrefix(string(msg), "EE") { - d.notifyExcludingSender([]byte(msg), c) + if len(m) > 2 && strings.HasPrefix(m, "EE") { + d.notifyExcludingSender([]byte(m), conn) } - // Send the message to dispatch to the frontend - result, err := d.dispatcher.ProcessMessage(string(msg), d) + result, err := d.dispatcher.ProcessMessage(m, d) if err != nil { d.logger.Error(err.Error()) } + if result != "" { locker.Lock() - if err = websocket.Message.Send(c, result); err != nil { - locker.Unlock() - break + defer locker.Unlock() + if err := conn.WriteMessage(websocket.TextMessage, []byte(result)); err != nil { + d.logger.Error("Websocket write message failed %v", err) } - locker.Unlock() } - } - }).ServeHTTP(c.Response(), c.Request()) + }(msg) + } + return nil } @@ -222,7 +241,7 @@ func (d *DevWebServer) broadcast(message string) { return } locker.Lock() - err := websocket.Message.Send(client, message) + err := client.WriteMessage(websocket.TextMessage, []byte(message)) if err != nil { locker.Unlock() d.logger.Error(err.Error()) @@ -256,7 +275,7 @@ func (d *DevWebServer) broadcastExcludingSender(message string, sender *websocke return } locker.Lock() - err := websocket.Message.Send(client, message) + err := client.WriteMessage(websocket.TextMessage, []byte(message)) if err != nil { locker.Unlock() d.logger.Error(err.Error()) diff --git a/v2/internal/frontend/dispatcher/dispatcher.go b/v2/internal/frontend/dispatcher/dispatcher.go index 60a99f467..24a43cfef 100644 --- a/v2/internal/frontend/dispatcher/dispatcher.go +++ b/v2/internal/frontend/dispatcher/dispatcher.go @@ -11,38 +11,42 @@ import ( ) type Dispatcher struct { - log *logger.Logger - bindings *binding.Bindings - events frontend.Events - bindingsDB *binding.DB - ctx context.Context - errfmt options.ErrorFormatter + log *logger.Logger + bindings *binding.Bindings + events frontend.Events + bindingsDB *binding.DB + ctx context.Context + errfmt options.ErrorFormatter + disablePanicRecovery bool } -func NewDispatcher(ctx context.Context, log *logger.Logger, bindings *binding.Bindings, events frontend.Events, errfmt options.ErrorFormatter) *Dispatcher { +func NewDispatcher(ctx context.Context, log *logger.Logger, bindings *binding.Bindings, events frontend.Events, errfmt options.ErrorFormatter, disablePanicRecovery bool) *Dispatcher { return &Dispatcher{ - log: log, - bindings: bindings, - events: events, - bindingsDB: bindings.DB(), - ctx: ctx, - errfmt: errfmt, + log: log, + bindings: bindings, + events: events, + bindingsDB: bindings.DB(), + ctx: ctx, + errfmt: errfmt, + disablePanicRecovery: disablePanicRecovery, } } func (d *Dispatcher) ProcessMessage(message string, sender frontend.Frontend) (_ string, err error) { - defer func() { - if e := recover(); e != nil { - if errPanic, ok := e.(error); ok { - err = errPanic - } else { - err = fmt.Errorf("%v", e) + if !d.disablePanicRecovery { + defer func() { + if e := recover(); e != nil { + if errPanic, ok := e.(error); ok { + err = errPanic + } else { + err = fmt.Errorf("%v", e) + } } - } - if err != nil { - d.log.Error("process message error: %s -> %s", message, err) - } - }() + if err != nil { + d.log.Error("process message error: %s -> %s", message, err) + } + }() + } if message == "" { return "", errors.New("No message to process") diff --git a/v2/internal/system/operatingsystem/os.go b/v2/internal/system/operatingsystem/os.go index 39f1de8e0..028a97b2e 100644 --- a/v2/internal/system/operatingsystem/os.go +++ b/v2/internal/system/operatingsystem/os.go @@ -2,9 +2,10 @@ package operatingsystem // OS contains information about the operating system type OS struct { - ID string - Name string - Version string + ID string + Name string + Version string + Branding string } // Info retrieves information about the current platform diff --git a/v2/internal/system/operatingsystem/os_windows.go b/v2/internal/system/operatingsystem/os_windows.go index 38ea43a12..a9aa05a92 100644 --- a/v2/internal/system/operatingsystem/os_windows.go +++ b/v2/internal/system/operatingsystem/os_windows.go @@ -4,10 +4,44 @@ package operatingsystem import ( "fmt" + "strings" + "syscall" + "unsafe" + "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" ) +func stripNulls(str string) string { + // Split the string into substrings at each null character + substrings := strings.Split(str, "\x00") + + // Join the substrings back into a single string + strippedStr := strings.Join(substrings, "") + + return strippedStr +} + +func mustStringToUTF16Ptr(input string) *uint16 { + input = stripNulls(input) + result, err := syscall.UTF16PtrFromString(input) + if err != nil { + panic(err) + } + return result +} + +func getBranding() string { + var modBranding = syscall.NewLazyDLL("winbrand.dll") + var brandingFormatString = modBranding.NewProc("BrandingFormatString") + + windowsLong := mustStringToUTF16Ptr("%WINDOWS_LONG%\x00") + ret, _, _ := brandingFormatString.Call( + uintptr(unsafe.Pointer(windowsLong)), + ) + return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(ret))) +} + func platformInfo() (*OS, error) { // Default value var result OS @@ -27,6 +61,7 @@ func platformInfo() (*OS, error) { result.Name = productName result.Version = fmt.Sprintf("%s (Build: %s)", releaseId, currentBuild) result.ID = displayVersion + result.Branding = getBranding() return &result, key.Close() } diff --git a/v2/internal/typescriptify/typescriptify.go b/v2/internal/typescriptify/typescriptify.go index 95376b2f4..216fba820 100644 --- a/v2/internal/typescriptify/typescriptify.go +++ b/v2/internal/typescriptify/typescriptify.go @@ -40,9 +40,7 @@ const ( jsVariableNameRegex = `^([A-Z]|[a-z]|\$|_)([A-Z]|[a-z]|[0-9]|\$|_)*$` ) -var ( - jsVariableUnsafeChars = regexp.MustCompile(`[^A-Za-z0-9_]`) -) +var jsVariableUnsafeChars = regexp.MustCompile(`[^A-Za-z0-9_]`) func nameTypeOf(typeOf reflect.Type) string { tname := typeOf.Name() @@ -277,6 +275,7 @@ func (t *typeScriptClassBuilder) AddMapField(fieldName string, field reflect.Str valueType := field.Type.Elem() valueTypeName := nameTypeOf(valueType) valueTypeSuffix := "" + valueTypePrefix := "" if valueType.Kind() == reflect.Ptr { valueType = valueType.Elem() valueTypeName = nameTypeOf(valueType) @@ -289,7 +288,8 @@ func (t *typeScriptClassBuilder) AddMapField(fieldName string, field reflect.Str } valueType = valueType.Elem() valueTypeName = nameTypeOf(valueType) - valueTypeSuffix = strings.Repeat("[]", arrayDepth) + valueTypeSuffix = strings.Repeat(">", arrayDepth) + valueTypePrefix = strings.Repeat("Array<", arrayDepth) } if valueType.Kind() == reflect.Ptr { valueType = valueType.Elem() @@ -325,10 +325,10 @@ func (t *typeScriptClassBuilder) AddMapField(fieldName string, field reflect.Str fieldName = fmt.Sprintf(`"%s"?`, strippedFieldName) } } - t.fields = append(t.fields, fmt.Sprintf("%s%s: Record<%s, %s>;", t.indent, fieldName, keyTypeStr, valueTypeName+valueTypeSuffix)) + t.fields = append(t.fields, fmt.Sprintf("%s%s: Record<%s, %s>;", t.indent, fieldName, keyTypeStr, valueTypePrefix+valueTypeName+valueTypeSuffix)) if valueType.Kind() == reflect.Struct { t.constructorBody = append(t.constructorBody, fmt.Sprintf("%s%sthis%s = this.convertValues(source[\"%s\"], %s, true);", - t.indent, t.indent, dotField, strippedFieldName, t.prefix+valueTypeName+valueTypeSuffix+t.suffix)) + t.indent, t.indent, dotField, strippedFieldName, t.prefix+valueTypePrefix+valueTypeName+valueTypeSuffix+t.suffix)) } else { t.constructorBody = append(t.constructorBody, fmt.Sprintf("%s%sthis%s = source[\"%s\"];", t.indent, t.indent, dotField, strippedFieldName)) diff --git a/v2/pkg/buildassets/build/windows/installer/wails_tools.nsh b/v2/pkg/buildassets/build/windows/installer/wails_tools.nsh index f9c0f8852..2f6d32195 100644 --- a/v2/pkg/buildassets/build/windows/installer/wails_tools.nsh +++ b/v2/pkg/buildassets/build/windows/installer/wails_tools.nsh @@ -158,7 +158,7 @@ RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}" ${If} ${REQUEST_EXECUTION_LEVEL} == "user" # If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed - ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" + ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" ${If} $0 != "" Goto ok ${EndIf} diff --git a/v2/pkg/commands/build/build.go b/v2/pkg/commands/build/build.go index f247f2db1..b4e83dd69 100644 --- a/v2/pkg/commands/build/build.go +++ b/v2/pkg/commands/build/build.go @@ -69,6 +69,7 @@ type Options struct { Obfuscated bool // Indicates that bound methods should be obfuscated GarbleArgs string // The arguments for Garble SkipBindings bool // Skip binding generation + SkipEmbedCreate bool // Skip creation of embed files } // Build the project! @@ -120,8 +121,10 @@ func Build(options *Options) (string, error) { } // Create embed directories if they don't exist - if err := CreateEmbedDirectories(cwd, options); err != nil { - return "", err + if !options.SkipEmbedCreate { + if err := CreateEmbedDirectories(cwd, options); err != nil { + return "", err + } } // Generate bindings diff --git a/v2/pkg/options/options.go b/v2/pkg/options/options.go index 282a25691..082ac48cd 100644 --- a/v2/pkg/options/options.go +++ b/v2/pkg/options/options.go @@ -98,6 +98,9 @@ type App struct { // DragAndDrop options for drag and drop behavior DragAndDrop *DragAndDrop + + // DisablePanicRecovery disables the panic recovery system in messages processing + DisablePanicRecovery bool } type ErrorFormatter func(error) any diff --git a/website/blog/2025-03-16-security-incident-response.mdx b/website/blog/2025-03-16-security-incident-response.mdx new file mode 100644 index 000000000..e9903c570 --- /dev/null +++ b/website/blog/2025-03-16-security-incident-response.mdx @@ -0,0 +1,89 @@ +--- +slug: security-incident-response-march-2025 +title: Proactive Security Response - GitHub Actions Supply Chain Attack +authors: [leaanthony] +tags: [wails, security] +--- + +
+ Security Shield +
+
+ +:::note TL;DR +**Good news! Wails was NOT affected by this security incident.** Our thorough investigation confirmed that no secrets were leaked, and the Wails codebase and releases remain completely secure. We've already taken proactive measures to further strengthen our security posture. +::: + +## Introduction + +On 15th March 2025 (AEST), the Wails team was alerted to a security incident involving the `tj-actions/changed-files` GitHub Action. This widely-used action (with over 23,000 repositories depending on it) was compromised in a supply chain attack. While this action was used in some of our CI/CD workflows, we're pleased to confirm that Wails remained fully protected throughout. + +This post shares the details of the incident, our response, and the additional safeguards we've implemented to ensure the continued security of the Wails project. + +## Incident Details + +The security company StepSecurity [reported](https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised) that the `tj-actions/changed-files` GitHub Action was compromised beginning around 9:00 AM March 14th, 2025 Pacific Time (4:00 PM UTC). + +In this attack, malicious code was injected into the action that attempted to dump CI/CD secrets from GitHub Actions runner processes into public logs. The attackers modified the action's code and retroactively updated multiple version tags to reference the malicious commit. + +## Our Proactive Assessment + +Upon learning this, we immediately launched a comprehensive assessment of our systems: + +1. We identified the following Wails workflows that were using the action: + - For Wails v2: `pr-v2.yml` and `upload-source-documents.yml` + - For Wails v3: `pr-v3.yml`, `publish-npm.yml`, and `upload-source-documents.yml` + +2. Our security team conducted a thorough review of all workflow logs for the affected actions during the time period of the compromise. + +3. We're happy to confirm that **no secrets were leaked** in any of our workflow logs, and the Wails codebase remained completely secure. + +## Action Taken + +We took immediate steps to address this situation: + +1. We swiftly replaced all instances of the affected `tj-actions/changed-files` action with the secure alternative `step-security/changed-files` provided by StepSecurity. + +2. As an extra precautionary measure, we temporarily removed all secrets from our GitHub Actions workflows. + +## What This Means for You + +We want to reassure our community that: + +1. The Wails codebase was never compromised in any way. +2. No malicious code was introduced into any Wails releases. +3. This situation only potentially affected our CI/CD pipeline, not the actual Wails source code or releases. +4. No sensitive information or secrets were exposed during this time. + +**In short: All Wails releases remain secure and trustworthy, and no action is required on your part.** + +## Strengthening Our Security Posture + +To minimise exposure to similar potential incidents in the future, we're enhancing our security practices by: + +1. Implementing stricter version pinning for all third-party actions used in our workflows, preferably pinning to specific commit hashes rather than version tags. + +2. Establishing a regular security review process for our CI/CD pipelines and dependencies. + +3. Exploring the use of additional security tools like StepSecurity's Harden-Runner to provide enhanced protection for our GitHub Actions workflows. + +4. Developing a more comprehensive security incident response plan to ensure we can respond quickly and effectively to any future security concerns. + +It's worth noting that the Wails project already employs several security tools as part of our development process: + +- **Semgrep**: We use Semgrep for static code analysis to identify potential security vulnerabilities and code quality issues. +- **Snyk**: We employ Snyk to continuously monitor our dependencies for known vulnerabilities and receive alerts when security patches are needed. + +These existing security measures, combined with our enhanced preventative steps, demonstrate our ongoing commitment to maintaining the security and integrity of the Wails project. + +## Moving Forward + +The security of the Wails project and the trust of our community are our highest priorities. We remain committed to transparency and will continue to promptly address any security concerns that arise. + +We would like to thank StepSecurity for their quick response in identifying this issue and providing a secure alternative action. + +If you have any questions or concerns about this, please don't hesitate to reach out to us on [GitHub](https://github.com/wailsapp/wails) or [Discord](https://discord.gg/JDdSxwjhGf). We're always here to help. diff --git a/website/docs/community/templates.mdx b/website/docs/community/templates.mdx index fdd2778c4..088b64a68 100644 --- a/website/docs/community/templates.mdx +++ b/website/docs/community/templates.mdx @@ -41,6 +41,7 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for - [wails-template-nextjs](https://github.com/LGiki/wails-template-nextjs) - A template using Next.js and TypeScript - [wails-template-nextjs-app-router](https://github.com/thisisvk-in/wails-template-nextjs-app-router) - A template using Next.js and TypeScript with App router - [wails-vite-react-ts-tailwind-template](https://github.com/hotafrika/wails-vite-react-ts-tailwind-template) - A template for React + TypeScript + Vite + TailwindCSS +- [Wails-vite-ts-tailwindcss-shadcn-template-2025](https://github.com/darkb0ts/Wails-vite-ts-tailwindcss-shadcn-template-2025) - A template for React + TypeScript + Vite - [wails-vite-react-ts-tailwind-shadcnui-template](https://github.com/Mahcks/wails-vite-react-tailwind-shadcnui-ts) - A template with Vite, React, TypeScript, TailwindCSS, and shadcn/ui - [wails-nextjs-tailwind-template](https://github.com/kairo913/wails-nextjs-tailwind-template) - A template using Next.js and Typescript with TailwindCSS diff --git a/website/docs/reference/cli.mdx b/website/docs/reference/cli.mdx index dc816a56b..84aba8fd8 100644 --- a/website/docs/reference/cli.mdx +++ b/website/docs/reference/cli.mdx @@ -73,6 +73,7 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for | -race | Build with Go's race detector | | | -s | Skip building the frontend | | | -skipbindings | Skip bindings generation | | +| -skipembedcreate | Skip automatic creation of non-existent embed directories and gitkeep files | | | -tags "extra tags" | Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated | | | -trimpath | Remove all file system paths from the resulting executable. | | | -u | Updates your project's `go.mod` to use the same version of Wails as the CLI | | @@ -203,6 +204,7 @@ Your system is ready for Wails development! | -s | Skip building the frontend | false | | -save | Saves the given `assetdir`, `reloaddirs`, `wailsjsdir`, `debounce`, `devserver` and `frontenddevserverurl` flags in `wails.json` to become the defaults for subsequent invocations. | | | -skipbindings | Skip bindings generation | | +| -skipembedcreate | Skip automatic creation of non-existent embed directories and gitkeep files | | | -tags "extra tags" | Build tags to pass to compiler (quoted and space separated) | | | -v | Verbosity level (0 - silent, 1 - standard, 2 - verbose) | 1 | | -wailsjsdir | The directory to generate the generated Wails JS modules | Value in `wails.json` | diff --git a/website/docs/reference/options.mdx b/website/docs/reference/options.mdx index efcc67961..9d999f421 100644 --- a/website/docs/reference/options.mdx +++ b/website/docs/reference/options.mdx @@ -487,6 +487,13 @@ services of Apple and Microsoft. Name: EnableFraudulentWebsiteDetection
Type: `bool` +### DisablePanicRecovery + +DisablePanicRecovery disables the automatic recovery from panics in message processing. By default, Wails will recover from panics in message processing and log the error. If you want to handle panics yourself, set this to `true`. + +Name: DisablePanicRecovery
+Type: `bool` + ### Bind A slice of struct instances defining methods that need to be bound to the frontend. diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 9d3c9d7a5..b738a052b 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -20,7 +20,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Updated recommendation for Svelte router in [#4085](https://github.com/wailsapp/wails/pull/4085) by [@benmccann](https://github.com/benmccann) +- Updated documentation to clarify `WebviewGpuPolicy` default behavior on Linux in [#4162](https://github.com/wailsapp/wails/pull/4162) by [@brianetaveras](https://github.com/brianetaveras) +### Added +- Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) +- Added `-skipembedcreate` flag to build and dev command to improve compile and recompile speed [#4143](https://github.com/wailsapp/wails/pull/4143) by @josStorer +- Added `DisablePanicRecovery` option to allow handle panics manually [#4136](https://github.com/wailsapp/wails/pull/4136) by [@APshenkin](https://github.com/APshenkin) + +### Fixed +- Fixed typescript generation of maps with key of array of structs by @joshuapare in [#4209](https://github.com/wailsapp/wails/pull/4209) +- Fixed -m build flag for dev command not working when recompiling in [#4141](https://github.com/wailsapp/wails/pull/4141) by @josStorer +- Fixed window restoration behavior after minimization by @superDingda in [#4109](https://github.com/wailsapp/wails/issues/4109) +- Fixed excessive console logging after updating to v2.10.1 by @superDingda in [#4111](https://github.com/wailsapp/wails/issues/4111) +- Fixed incorrect Webview2 reg key in HKCU path by @leaanthony. +- Fixed long WebSocket messages being truncated in `wails dev` by @superDingda in [#4211](https://github.com/wailsapp/wails/issues/4211) ## v2.10.1 - 2025-02-24 diff --git a/website/static/img/blog/shield.png b/website/static/img/blog/shield.png new file mode 100644 index 000000000..7b715f8f4 Binary files /dev/null and b/website/static/img/blog/shield.png differ diff --git a/website/static/img/sponsors.svg b/website/static/img/sponsors.svg index 71215912a..107d43ba4 100644 --- a/website/static/img/sponsors.svg +++ b/website/static/img/sponsors.svg @@ -1,5 +1,5 @@ - + diff --git a/website/versioned_docs/version-v2.10/howdoesitwork.mdx b/website/versioned_docs/version-v2.10/howdoesitwork.mdx index 48243f4eb..69134ac24 100644 --- a/website/versioned_docs/version-v2.10/howdoesitwork.mdx +++ b/website/versioned_docs/version-v2.10/howdoesitwork.mdx @@ -33,7 +33,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" - "github.com/wailsapp/wails/v2/pkg/options/assetserver" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -150,7 +150,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" - "github.com/wailsapp/wails/v2/pkg/options/assetserver" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist diff --git a/website/versioned_docs/version-v2.10/reference/options.mdx b/website/versioned_docs/version-v2.10/reference/options.mdx index efcc67961..ed7ccda01 100644 --- a/website/versioned_docs/version-v2.10/reference/options.mdx +++ b/website/versioned_docs/version-v2.10/reference/options.mdx @@ -126,7 +126,7 @@ func main() { Linux: &linux.Options{ Icon: icon, WindowIsTranslucent: false, - WebviewGpuPolicy: linux.WebviewGpuPolicyAlways, + WebviewGpuPolicy: linux.WebviewGpuPolicyNever, ProgramName: "wails" }, Debug: options.Debug{ @@ -1066,7 +1066,10 @@ This option is used for determining the webview's hardware acceleration policy. Name: WebviewGpuPolicy
Type: [`options.WebviewGpuPolicy`](#webviewgpupolicy-type)
-Default: `WebviewGpuPolicyAlways` +Default (Windows, macOS): `WebviewGpuPolicyAlways`
+Default (Linux): Due to [#2977](https://github.com/wailsapp/wails/issues/2977,), if `options.Linux` is nil + in the call to `wails.Run()`, `WebviewGpuPolicy` is set by default to `WebviewGpuPolicyNever`. You can override this behavior by passing a non-nil `Options` and set `WebviewGpuPolicy` as needed. + ##### WebviewGpuPolicy type