Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/auto_assign.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
addReviewers: true
reviewers:
- mzack9999

numberOfReviewers: 1
skipKeywords:
- '@dependabot'
44 changes: 44 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:

# Maintain dependencies for go modules
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
target-branch: "main"
commit-message:
prefix: "chore"
include: "scope"
allow:
- dependency-name: "github.com/projectdiscovery/*"
groups:
modules:
patterns: ["github.com/projectdiscovery/*"]
labels:
- "Type: Maintenance"

# # Maintain dependencies for docker
# - package-ecosystem: "docker"
# directory: "/"
# schedule:
# interval: "weekly"
# target-branch: "main"
# commit-message:
# prefix: "chore"
# include: "scope"
#
# # Maintain dependencies for GitHub Actions
# - package-ecosystem: "github-actions"
# directory: "/"
# schedule:
# interval: "weekly"
# target-branch: "main"
# commit-message:
# prefix: "chore"
# include: "scope"
33 changes: 33 additions & 0 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: 🔨 Build Test

on:
pull_request:
workflow_dispatch:

jobs:
lint:
name: "Lint"
if: "${{ !endsWith(github.actor, '[bot]') }}"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/golangci-lint@v1

build:
name: Test Builds
needs: ["lint"]
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1

- name: Test
run: go test ./...

- name: Race Condition Tests
if: ${{ matrix.os != 'windows-latest' }} # false positives in windows
run: go test -race ./...
6 changes: 3 additions & 3 deletions cmdexec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"os/exec"

"github.com/projectdiscovery/gozero/types"
errorutil "github.com/projectdiscovery/utils/errors"
"github.com/projectdiscovery/utils/errkit"
)

// Command is a command to execute.
Expand Down Expand Up @@ -74,15 +74,15 @@ func (c *Command) Execute(ctx context.Context) (*types.Result, error) {
if err := cmd.Start(); err != nil {
// this error indicates that command did not start at all (e.g. binary not found)
// or something similar
return res, errorutil.NewWithErr(err).Msgf("failed to start command got: %v", res.Stderr.String())
return res, errkit.WithMessagef(err, "failed to start command got: %v", res.Stderr.String())
}

if err := cmd.Wait(); err != nil {
if execErr, ok := err.(*exec.ExitError); ok {
res.SetExitError(execErr)
}
// this error indicates that command started but exited with non-zero exit code
return res, errorutil.NewWithErr(err).Msgf("failed to exec command got: %v", res.Stderr.String())
return res, errkit.WithMessagef(err, "failed to exec command got: %v", res.Stderr.String())
}
return res, nil
}
Expand Down
128 changes: 128 additions & 0 deletions examples/bubblewrap_sandbox/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//go:build linux

package main

import (
"context"
"fmt"
"log"
"os"
"path/filepath"

"github.com/projectdiscovery/gozero/sandbox"
osutils "github.com/projectdiscovery/utils/os"
)

func main() {
ctx := context.Background()

if !osutils.IsLinux() {
log.Printf("This example is only supported on Linux")
return
}

fmt.Println("=== Bubblewrap Sandbox Example ===")
fmt.Println()

// Create bubblewrap configuration with static settings
config := &sandbox.BubblewrapConfiguration{
TempDir: filepath.Join(os.TempDir(), "gozero-bubblewrap"),
UnsharePID: true,
UnshareIPC: true,
UnshareNetwork: true,
UnshareUTS: true,
UnshareUser: true,
NewSession: true,
HostFilesystem: true,
Environment: map[string]string{
"PATH": "/usr/bin:/bin",
},
}

// Create bubblewrap sandbox
bwrap, err := sandbox.NewBubblewrapSandbox(ctx, config)
if err != nil {
log.Fatalf("Failed to create bubblewrap sandbox: %v", err)
}
defer bwrap.Clear()

fmt.Println("1. Running a simple command in the sandbox...")
result, err := bwrap.Run(ctx, "echo 'Hello from bubblewrap sandbox!'")
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Exit Code: %d\n", result.GetExitCode())
fmt.Printf("Output: %s\n", result.Stdout.String())
if result.Stderr.Len() > 0 {
fmt.Printf("Errors: %s\n", result.Stderr.String())
}
}
fmt.Println("---")

fmt.Println("2. Running ls command in the sandbox...")
result, err = bwrap.Run(ctx, "ls /")
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Exit Code: %d\n", result.GetExitCode())
fmt.Printf("Output: %s\n", result.Stdout.String())
if result.Stderr.Len() > 0 {
fmt.Printf("Errors: %s\n", result.Stderr.String())
}
}
fmt.Println("---")

fmt.Println("3. Running env command to see sandbox environment...")
result, err = bwrap.Run(ctx, "env")
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Exit Code: %d\n", result.GetExitCode())
fmt.Printf("Output: %s\n", result.Stdout.String())
if result.Stderr.Len() > 0 {
fmt.Printf("Errors: %s\n", result.Stderr.String())
}
}
fmt.Println("---")

fmt.Println("4. Running Python source code in the sandbox...")
pyCode := `print("Hello from Python in bubblewrap!")
print("Python is working in the sandbox!")
import sys
print(f"Python version: {sys.version}")
`
result, err = bwrap.RunSource(ctx, pyCode)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Exit Code: %d\n", result.GetExitCode())
fmt.Printf("Output: %s\n", result.Stdout.String())
if result.Stderr.Len() > 0 {
fmt.Printf("Errors: %s\n", result.Stderr.String())
}
}
fmt.Println("---")

fmt.Println("5. Using ExecuteWithOptions with per-command configuration...")
options := &sandbox.BubblewrapCommandOptions{
Command: "bash",
Args: []string{"-c", "echo 'Hello from per-command options!' && ls /src"},
CommandBinds: []sandbox.BindMount{
{HostPath: "/usr/bin", SandboxPath: "/usr/bin"},
},
WorkingDir: "/src",
}
result, err = bwrap.ExecuteWithOptions(ctx, options)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Exit Code: %d\n", result.GetExitCode())
fmt.Printf("Output: %s\n", result.Stdout.String())
if result.Stderr.Len() > 0 {
fmt.Printf("Errors: %s\n", result.Stderr.String())
}
}
fmt.Println("---")

fmt.Println("=== Bubblewrap sandbox test completed ===")
}
127 changes: 127 additions & 0 deletions examples/docker_sandbox/bash_source/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package main

import (
"context"
"fmt"
"log"
"time"

"github.com/projectdiscovery/gozero/sandbox"
osutils "github.com/projectdiscovery/utils/os"
)

func main() {
ctx := context.Background()

if !osutils.IsLinux() {
log.Printf("This example is only supported on Linux")
return
}

// Create Docker sandbox configuration for shell execution
config := &sandbox.DockerConfiguration{
Image: "alpine:latest",
WorkingDir: "/tmp",
Environment: map[string]string{
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
},
NetworkMode: "bridge",
NetworkDisabled: false,
User: "root",
Memory: "128m",
CPULimit: "0.5",
Timeout: 30 * time.Second,
Remove: true,
}

// Create Docker sandbox
sandboxInstance, err := sandbox.NewDockerSandbox(ctx, config)
if err != nil {
log.Fatalf("Failed to create Docker sandbox: %v", err)
}
defer func() {
err := sandboxInstance.Clear()
if err != nil {
log.Fatalf("Failed to clear Docker sandbox: %v", err)
}
}()

// Test shell scripts using RunSource
scripts := []struct {
name string
script string
}{
{
name: "Simple Hello World",
script: `#!/bin/sh
echo "Hello from shell script!"
echo "Current user: $(whoami)"
echo "Current directory: $(pwd)"
echo "System info: $(uname -a)"
`,
},
{
name: "File Operations",
script: `#!/bin/sh
echo "Creating test files..."
echo "File 1 content" > /tmp/test1.txt
echo "File 2 content" > /tmp/test2.txt
echo "Files created:"
ls -la /tmp/test*.txt
echo "File contents:"
cat /tmp/test1.txt
cat /tmp/test2.txt
`,
},
{
name: "System Information",
script: `#!/bin/sh
echo "=== System Information ==="
echo "Hostname: $(hostname)"
echo "User: $(whoami)"
echo "UID: $(id -u)"
echo "GID: $(id -g)"
echo "Groups: $(id -G)"
echo "Home: $HOME"
echo "Shell: $SHELL"
echo "PATH: $PATH"
echo ""
echo "=== Memory Information ==="
cat /proc/meminfo | head -5
echo ""
echo "=== CPU Information ==="
cat /proc/cpuinfo | head -10
`,
},
{
name: "Network Test",
script: `#!/bin/sh
echo "=== Network Configuration ==="
echo "Hostname: $(hostname)"
echo "IP addresses:"
ip addr show 2>/dev/null || ifconfig 2>/dev/null || echo "Network tools not available"
echo ""
echo "=== DNS Resolution Test ==="
nslookup google.com 2>/dev/null || echo "DNS resolution not available"
`,
},
}

for _, test := range scripts {
fmt.Printf("\n=== Running: %s ===\n", test.name)
result, err := sandboxInstance.RunSource(ctx, test.script, "sh")
if err != nil {
fmt.Printf("Error: %v\n", err)
continue
}

fmt.Printf("Exit Code: %d\n", result.GetExitCode())
fmt.Printf("Stdout:\n%s\n", result.Stdout.String())
if result.Stderr.Len() > 0 {
fmt.Printf("Stderr:\n%s\n", result.Stderr.String())
}
fmt.Println("---")
}

fmt.Println("\n=== Shell source execution test completed ===")
}
Loading
Loading