Improve dependency resolver and fix critical bugs
- Add comprehensive Debian dependency resolver (400+ package mappings) - Implement automatic recursive dependency resolution for build dependencies - Fix logic errors in install.go: allowFailure handling, ensureBuildDependency - Fix install-script.sh PKGDIR environment variable handling - Fix hardcoded /tmp/install-script.sh path in autoRecipeFromDebian - Fix wrong yyjson URL - Increase build timeout from 30min to 2 hours - Improve command validation security - Fix temporary directory leaks in installer - Update .gitignore to include zsvo-test binary
This commit is contained in:
parent
fc315b1c2c
commit
fbe74e8a87
5 changed files with 1311 additions and 40 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,5 +1,6 @@
|
||||||
# Binary executables
|
# Binary executables
|
||||||
zsvo
|
zsvo
|
||||||
|
zsvo-test
|
||||||
pkg-manager
|
pkg-manager
|
||||||
|
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
|
|
|
||||||
105
cmd/install.go
105
cmd/install.go
|
|
@ -191,6 +191,7 @@ type autoBuildSession struct {
|
||||||
toolRoot string
|
toolRoot string
|
||||||
autoBuildDeps bool
|
autoBuildDeps bool
|
||||||
resolver *debian.Resolver
|
resolver *debian.Resolver
|
||||||
|
depResolver *debian.DependencyResolver
|
||||||
builder *builder.Builder
|
builder *builder.Builder
|
||||||
toolInstaller *installer.Installer
|
toolInstaller *installer.Installer
|
||||||
builtPackages map[string]string
|
builtPackages map[string]string
|
||||||
|
|
@ -207,6 +208,7 @@ func newAutoBuildSession(workDir string, autoBuildDeps bool) *autoBuildSession {
|
||||||
toolRoot: filepath.Join(workDir, "bootstrap-root"),
|
toolRoot: filepath.Join(workDir, "bootstrap-root"),
|
||||||
autoBuildDeps: autoBuildDeps,
|
autoBuildDeps: autoBuildDeps,
|
||||||
resolver: debian.NewResolver(),
|
resolver: debian.NewResolver(),
|
||||||
|
depResolver: debian.NewDependencyResolver(),
|
||||||
builder: b,
|
builder: b,
|
||||||
toolInstaller: installer.NewInstaller(filepath.Join(workDir, "bootstrap-root")),
|
toolInstaller: installer.NewInstaller(filepath.Join(workDir, "bootstrap-root")),
|
||||||
builtPackages: make(map[string]string),
|
builtPackages: make(map[string]string),
|
||||||
|
|
@ -302,14 +304,66 @@ func (s *autoBuildSession) buildPackageWithFallback(requestName string, asBuildD
|
||||||
|
|
||||||
fmt.Printf("Building %s from %s...\n", rcp.GetPackageName(), srcInfo.DSCURL)
|
fmt.Printf("Building %s from %s...\n", rcp.GetPackageName(), srcInfo.DSCURL)
|
||||||
|
|
||||||
// ВАЖНО: Мы НЕ собираем Debian зависимости!
|
// Авто-разрешение зависимостей: строим все Build-Depends рекурсивно
|
||||||
// Мы используем Debian только как источник исходников
|
if len(srcInfo.BuildDepends) > 0 && s.autoBuildDeps {
|
||||||
// Все зависимости должны быть уже установлены в LFS системе
|
fmt.Printf("🔍 Resolving %d build dependencies for %s...\n", len(srcInfo.BuildDepends), requestName)
|
||||||
if len(srcInfo.BuildDepends) > 0 {
|
|
||||||
fmt.Printf("⚠️ Found Debian Build-Depends: %s\n", strings.Join(srcInfo.BuildDepends, ", "))
|
// Collect dependencies that need to be built
|
||||||
fmt.Printf("ℹ️ In LFS, make sure these dependencies are available in your toolchain\n")
|
depsToBuild := make([]string, 0)
|
||||||
|
for _, dep := range srcInfo.BuildDepends {
|
||||||
|
depName := extractPackageNameFromConstraint(dep)
|
||||||
|
if depName == "" {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this is a Debian-specific package that should be skipped
|
||||||
|
sourcePkg := mapDebianPackageToSource(depName)
|
||||||
|
if sourcePkg == "" {
|
||||||
|
// Try comprehensive resolver
|
||||||
|
sourcePkg, _ = s.depResolver.BinaryToSource(depName)
|
||||||
|
if sourcePkg == "" {
|
||||||
|
fmt.Printf(" ⚠️ Skipping Debian-specific dependency: %s\n", depName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if already built or available
|
||||||
|
if _, built := s.builtPackages[sourcePkg]; built {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if toolAlreadyAvailable(sourcePkg) {
|
||||||
|
s.toolDepsReady[sourcePkg] = struct{}{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ready := s.toolDepsReady[sourcePkg]; ready {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
depsToBuild = append(depsToBuild, sourcePkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build all dependencies first (in order)
|
||||||
|
if len(depsToBuild) > 0 {
|
||||||
|
fmt.Printf("📦 Need to build %d dependencies: %s\n", len(depsToBuild), strings.Join(depsToBuild, ", "))
|
||||||
|
|
||||||
|
for _, dep := range depsToBuild {
|
||||||
|
// Recursive build with dependency tracking
|
||||||
|
_, err := s.buildPackageWithFallback(dep, true, false, append(stack, requestName))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" ⚠️ Failed to build dependency %s: %v (continuing anyway)\n", dep, err)
|
||||||
|
// Don't fail immediately - the main package might still build
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh environment after building dependencies
|
||||||
|
s.refreshBuildEnv()
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✅ All dependencies already available\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now build the main package
|
||||||
|
|
||||||
var buildErr error
|
var buildErr error
|
||||||
for attempt := 0; attempt < 2; attempt++ {
|
for attempt := 0; attempt < 2; attempt++ {
|
||||||
bar := newProgressUI(rcp.Name)
|
bar := newProgressUI(rcp.Name)
|
||||||
|
|
@ -335,16 +389,18 @@ func (s *autoBuildSession) buildPackageWithFallback(requestName string, asBuildD
|
||||||
}
|
}
|
||||||
|
|
||||||
builtPackage := filepath.Join(rcp.GetPackageDir(s.workDir), rcp.GetPackageFileName())
|
builtPackage := filepath.Join(rcp.GetPackageDir(s.workDir), rcp.GetPackageFileName())
|
||||||
s.builtPackages[rcp.Name] = builtPackage
|
|
||||||
|
|
||||||
if buildErr != nil {
|
if buildErr != nil {
|
||||||
if !allowFailure {
|
if !allowFailure {
|
||||||
return "", fmt.Errorf("failed to auto-build %s: %w", requestName, buildErr)
|
return "", fmt.Errorf("failed to auto-build %s: %w", requestName, buildErr)
|
||||||
}
|
}
|
||||||
if err := s.installBuildDependency(requestName, builtPackage); err != nil {
|
// allowFailure=true means we tolerate build failure, but we shouldn't try to install a broken package
|
||||||
return "", err
|
// Log and return empty - caller should handle gracefully
|
||||||
}
|
fmt.Printf("Warning: build of %s failed but allowFailure=true, skipping installation of broken package\n", requestName)
|
||||||
|
return "", fmt.Errorf("build failed for %s (allowFailure set): %w", requestName, buildErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.builtPackages[rcp.Name] = builtPackage
|
||||||
return builtPackage, nil
|
return builtPackage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,8 +409,12 @@ func (s *autoBuildSession) installSystemPackage(pkg string, currentBuildingPacka
|
||||||
// Try to resolve and build the package as a dependency
|
// Try to resolve and build the package as a dependency
|
||||||
fmt.Printf("Building system dependency %s through zsvo...\n", pkg)
|
fmt.Printf("Building system dependency %s through zsvo...\n", pkg)
|
||||||
|
|
||||||
// Map common Debian package names to source packages
|
// Use comprehensive Debian resolver to map binary package to source
|
||||||
sourcePkg := mapDebianPackageToSource(pkg)
|
sourcePkg, err := s.depResolver.BinaryToSource(pkg)
|
||||||
|
if err != nil {
|
||||||
|
// Fall back to built-in mapping for common packages
|
||||||
|
sourcePkg = mapDebianPackageToSource(pkg)
|
||||||
|
}
|
||||||
if sourcePkg == "" {
|
if sourcePkg == "" {
|
||||||
fmt.Printf("Skipping Debian-specific dependency: %s\n", pkg)
|
fmt.Printf("Skipping Debian-specific dependency: %s\n", pkg)
|
||||||
return nil // Skip Debian-specific packages
|
return nil // Skip Debian-specific packages
|
||||||
|
|
@ -367,7 +427,7 @@ func (s *autoBuildSession) installSystemPackage(pkg string, currentBuildingPacka
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to build the dependency
|
// Try to build the dependency
|
||||||
_, err := s.buildPackageWithFallback(sourcePkg, true, false, []string{})
|
_, err = s.buildPackageWithFallback(sourcePkg, true, false, []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to build system dependency %s (mapped from %s): %w", sourcePkg, pkg, err)
|
return fmt.Errorf("failed to build system dependency %s (mapped from %s): %w", sourcePkg, pkg, err)
|
||||||
}
|
}
|
||||||
|
|
@ -403,8 +463,12 @@ func (s *autoBuildSession) ensureBuildDependency(dep string, stack []string) err
|
||||||
fmt.Printf("Installing build dependency %s into %s...\n", dep, s.toolRoot)
|
fmt.Printf("Installing build dependency %s into %s...\n", dep, s.toolRoot)
|
||||||
packagePath, err := s.buildPackageWithFallback(dep, true, false, append(stack, dep))
|
packagePath, err := s.buildPackageWithFallback(dep, true, false, append(stack, dep))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Warning: failed to build dependency %s: %v (continuing anyway)\n", dep, err)
|
fmt.Printf("Warning: failed to build dependency %s: %v (skipping installation)\n", dep, err)
|
||||||
// Continue anyway instead of failing
|
return nil // Don't try to install a package that failed to build
|
||||||
|
}
|
||||||
|
if packagePath == "" {
|
||||||
|
fmt.Printf("Warning: build succeeded but no package path returned for %s (skipping installation)\n", dep)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return s.installBuildDependency(dep, packagePath)
|
return s.installBuildDependency(dep, packagePath)
|
||||||
}
|
}
|
||||||
|
|
@ -958,9 +1022,12 @@ func autoRecipeFromDebian(src *debian.SourceInfo) *recipe.Recipe {
|
||||||
version = "0"
|
version = "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use external install script to avoid shell escaping issues
|
// Use inline install commands instead of external script to avoid hardcoded paths
|
||||||
installCommands := []string{
|
installCommands := []string{
|
||||||
"PKGDIR={{pkgdir}} bash /tmp/install-script.sh",
|
"if [ -f build/cmake_install.cmake ]; then DESTDIR={{pkgdir}} cmake --install build; " +
|
||||||
|
"elif [ -f build/meson-private/coredata.dat ]; then DESTDIR={{pkgdir}} meson install -C build; " +
|
||||||
|
"elif [ -f Makefile ] || [ -f makefile ] || [ -f GNUmakefile ]; then make DESTDIR={{pkgdir}} PREFIX=/usr install; " +
|
||||||
|
"else mkdir -p {{pkgdir}}/usr/bin {{pkgdir}}/usr/lib {{pkgdir}}/usr/include; fi",
|
||||||
}
|
}
|
||||||
|
|
||||||
return &recipe.Recipe{
|
return &recipe.Recipe{
|
||||||
|
|
@ -974,8 +1041,8 @@ func autoRecipeFromDebian(src *debian.SourceInfo) *recipe.Recipe {
|
||||||
Build: []string{
|
Build: []string{
|
||||||
"if [ ! -f configure ] && [ -f autogen.sh ]; then sh ./autogen.sh --no-check ${ZSVO_AUTOGEN_ARGS}; fi; if [ -f configure ]; then ./configure --prefix=/usr ${ZSVO_CONFIGURE_FLAGS}; fi",
|
"if [ ! -f configure ] && [ -f autogen.sh ]; then sh ./autogen.sh --no-check ${ZSVO_AUTOGEN_ARGS}; fi; if [ -f configure ]; then ./configure --prefix=/usr ${ZSVO_CONFIGURE_FLAGS}; fi",
|
||||||
"# Download missing dependencies automatically",
|
"# Download missing dependencies automatically",
|
||||||
"if [ ! -f \"src/3rdparty/yyjson/yyjson.c\" ]; then echo \"Downloading yyjson...\" && mkdir -p src/3rdparty/yyjson && curl -L https://raw.githubusercontent.com/yyjson/yyjson.c/master/src/yyjson.c -o src/3rdparty/yyjson/yyjson.c; fi",
|
"if [ ! -f \"src/3rdparty/yyjson/yyjson.c\" ]; then echo \"Downloading yyjson...\" && mkdir -p src/3rdparty/yyjson && curl -L https://raw.githubusercontent.com/ibireme/yyjson/master/src/yyjson.c -o src/3rdparty/yyjson/yyjson.c; fi",
|
||||||
"if [ ! -f \"src/3rdparty/yyjson/yyjson.h\" ]; then echo \"Downloading yyjson header...\" && mkdir -p src/3rdparty/yyjson && curl -L https://raw.githubusercontent.com/yyjson/yyjson.c/master/src/yyjson.h -o src/3rdparty/yyjson/yyjson.h; fi",
|
"if [ ! -f \"src/3rdparty/yyjson/yyjson.h\" ]; then echo \"Downloading yyjson header...\" && mkdir -p src/3rdparty/yyjson && curl -L https://raw.githubusercontent.com/ibireme/yyjson/master/src/yyjson.h -o src/3rdparty/yyjson/yyjson.h; fi",
|
||||||
"if [ -f CMakeLists.txt ]; then cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DUNIX=1 -DCMAKE_DISABLE_FIND_PACKAGE_Win32=1 ${ZSVO_CMAKE_FLAGS} && cmake --build build -j${jobs} -- ${ZSVO_CMAKE_BUILD_FLAGS}; " +
|
"if [ -f CMakeLists.txt ]; then cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DUNIX=1 -DCMAKE_DISABLE_FIND_PACKAGE_Win32=1 ${ZSVO_CMAKE_FLAGS} && cmake --build build -j${jobs} -- ${ZSVO_CMAKE_BUILD_FLAGS}; " +
|
||||||
"elif [ -f meson.build ]; then meson setup build --prefix=/usr ${ZSVO_MESON_SETUP_ARGS} && meson compile -C build -j${jobs} ${ZSVO_MESON_COMPILE_ARGS}; " +
|
"elif [ -f meson.build ]; then meson setup build --prefix=/usr ${ZSVO_MESON_SETUP_ARGS} && meson compile -C build -j${jobs} ${ZSVO_MESON_COMPILE_ARGS}; " +
|
||||||
"elif [ -f Makefile ] || [ -f makefile ] || [ -f GNUmakefile ]; then make -j${jobs} ${ZSVO_MAKE_FLAGS}; fi",
|
"elif [ -f Makefile ] || [ -f makefile ] || [ -f GNUmakefile ]; then make -j${jobs} ${ZSVO_MAKE_FLAGS}; fi",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,13 @@
|
||||||
# Simple install script for auto-generated packages
|
# Simple install script for auto-generated packages
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
PKGDIR="$1"
|
# Use PKGDIR from environment, fallback to first argument for backward compatibility
|
||||||
|
PKGDIR="${PKGDIR:-$1}"
|
||||||
|
|
||||||
|
if [ -z "$PKGDIR" ]; then
|
||||||
|
echo "Error: PKGDIR not set. Usage: PKGDIR=/path/to/dest $0" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Installing to $PKGDIR"
|
echo "Installing to $PKGDIR"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -259,8 +259,8 @@ func (b *Builder) executeCommand(workDir, command string, env []string) error {
|
||||||
cmd.Dir = workDir
|
cmd.Dir = workDir
|
||||||
cmd.Env = env
|
cmd.Env = env
|
||||||
if b.quiet {
|
if b.quiet {
|
||||||
// Create a context with timeout to prevent hanging
|
// Create a context with timeout to prevent hanging - use 2 hours for large packages like gcc/llvm
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Hour)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, "sh", "-c", command)
|
cmd := exec.CommandContext(ctx, "sh", "-c", command)
|
||||||
|
|
@ -277,8 +277,8 @@ func (b *Builder) executeCommand(workDir, command string, env []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// For non-quiet mode, still set a reasonable timeout
|
// For non-quiet mode, still set a reasonable timeout - use 2 hours for large packages
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Hour)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
cmd = exec.CommandContext(ctx, "sh", "-c", command)
|
cmd = exec.CommandContext(ctx, "sh", "-c", command)
|
||||||
cmd.Dir = workDir
|
cmd.Dir = workDir
|
||||||
|
|
@ -614,25 +614,43 @@ func (b *Builder) validateSourceFiles(recipe *recipe.Recipe, sourceDir string) e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) validateCommand(command string) error {
|
func (b *Builder) validateCommand(command string) error {
|
||||||
// List of dangerous patterns to block
|
// Normalize for pattern matching
|
||||||
|
cmdLower := strings.ToLower(command)
|
||||||
|
|
||||||
|
// Remove common bypass attempts: quotes, spaces, backslashes for normalization
|
||||||
|
normalized := cmdLower
|
||||||
|
normalized = strings.ReplaceAll(normalized, `"`, "")
|
||||||
|
normalized = strings.ReplaceAll(normalized, `'`, "")
|
||||||
|
normalized = strings.ReplaceAll(normalized, ` `, "")
|
||||||
|
normalized = strings.ReplaceAll(normalized, `\`, "")
|
||||||
|
|
||||||
|
// List of dangerous patterns to block (check both original and normalized)
|
||||||
dangerousPatterns := []string{
|
dangerousPatterns := []string{
|
||||||
"rm -rf /",
|
"rm -rf /",
|
||||||
"rm -rf /*",
|
"rm -rf /*",
|
||||||
":(){ :|:& };:", // fork bomb
|
"rm -rf/",
|
||||||
|
`:(){:|:&};:`, // fork bomb (normalized)
|
||||||
|
":(){ :|:& };:", // fork bomb (original)
|
||||||
"chmod 777 /",
|
"chmod 777 /",
|
||||||
"chown root",
|
"chown root",
|
||||||
"sudo ",
|
"sudo ",
|
||||||
"su ",
|
"su -",
|
||||||
|
"> /dev/sda",
|
||||||
|
"> /dev/hda",
|
||||||
">/dev/sda",
|
">/dev/sda",
|
||||||
">/dev/hda",
|
">/dev/hda",
|
||||||
"mkfs",
|
"mkfs",
|
||||||
"format",
|
"format /",
|
||||||
"fdisk",
|
"fdisk",
|
||||||
// Additional patterns
|
|
||||||
"dd if=",
|
"dd if=",
|
||||||
"chmod -R 777 /",
|
"chmod -R 777 /",
|
||||||
"chown -R root",
|
"chown -R root",
|
||||||
"rm -rf /*",
|
"rm -rf /etc",
|
||||||
|
"rm -rf /usr",
|
||||||
|
"rm -rf /bin",
|
||||||
|
"rm -rf /sbin",
|
||||||
|
"rm -rf /lib",
|
||||||
|
"rm -rf /lib64",
|
||||||
"rm-rf/etc",
|
"rm-rf/etc",
|
||||||
"rm-rf/usr",
|
"rm-rf/usr",
|
||||||
"rm-rf/bin",
|
"rm-rf/bin",
|
||||||
|
|
@ -645,17 +663,25 @@ func (b *Builder) validateCommand(command string) error {
|
||||||
"poweroff",
|
"poweroff",
|
||||||
"init 0",
|
"init 0",
|
||||||
"init 6",
|
"init 6",
|
||||||
|
"eval",
|
||||||
|
"$(", // command substitution
|
||||||
|
"${", // parameter expansion
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdLower := strings.ToLower(command)
|
// Check against original
|
||||||
|
|
||||||
// Check for dangerous patterns
|
|
||||||
for _, pattern := range dangerousPatterns {
|
for _, pattern := range dangerousPatterns {
|
||||||
if strings.Contains(cmdLower, pattern) {
|
if strings.Contains(cmdLower, pattern) {
|
||||||
return fmt.Errorf("command contains potentially dangerous pattern: %s", pattern)
|
return fmt.Errorf("command contains potentially dangerous pattern: %s", pattern)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check against normalized (catches bypass attempts like 'rm -rf "/"' or 'rm -rf / ')
|
||||||
|
for _, pattern := range dangerousPatterns {
|
||||||
|
if strings.Contains(normalized, pattern) {
|
||||||
|
return fmt.Errorf("command contains potentially dangerous pattern (normalized): %s", pattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for suspicious characters that might indicate injection
|
// Check for suspicious characters that might indicate injection
|
||||||
suspiciousChars := []string{"\x00", "\r", "\n", "\t"}
|
suspiciousChars := []string{"\x00", "\r", "\n", "\t"}
|
||||||
for _, char := range suspiciousChars {
|
for _, char := range suspiciousChars {
|
||||||
|
|
@ -676,14 +702,12 @@ func (b *Builder) validateCommand(command string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for command substitution patterns that could bypass security
|
// Check for command substitution patterns that could bypass security
|
||||||
dangerousSubstitutions := []string{
|
// Backticks and $() are blocked
|
||||||
"`",
|
if strings.Contains(command, "`") {
|
||||||
"$|",
|
return fmt.Errorf("command contains potentially dangerous backtick substitution")
|
||||||
}
|
|
||||||
for _, pattern := range dangerousSubstitutions {
|
|
||||||
if strings.Contains(command, pattern) {
|
|
||||||
return fmt.Errorf("command contains potentially dangerous substitution: %s", pattern)
|
|
||||||
}
|
}
|
||||||
|
if strings.Contains(command, "$(") {
|
||||||
|
return fmt.Errorf("command contains potentially dangerous command substitution $()")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the command for audit purposes (only first 100 chars)
|
// Log the command for audit purposes (only first 100 chars)
|
||||||
|
|
|
||||||
1173
pkg/debian/dep_resolver.go
Normal file
1173
pkg/debian/dep_resolver.go
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue