mirror of
https://github.com/projectdiscovery/nuclei
synced 2026-06-08 16:50:47 +00:00
6f2ade6a9b
* fix(js): respect `allow-local-file-access` in `require` The goja `require() `function used the default host filesystem loader which let JavaScript templates import any local files even when `allow-local-file-access` was disabled. Pooled runtimes kept `require()` state around so a module loaded during a privileged execution could remain cached for a later restricted one. Rebuild the require registry per execution after setting the execution context, and route file- backed module loads to preserve native modules while enforcing the same sandbox rules (as `nuclei/fs`). Signed-off-by: Dwi Siswanto <git@dw1.io> * fix: cross-platform sandbox path checks Replace lexical prefix checks in the template file sandbox with a shared path containment helper that canonicalizes both paths before comparing them to prevent false rejections when the configured templates directory and the resolved file path differ only due to symlink expansion on macOS or path normalization on Windows. Apply the helper in `protocolstate.NormalizePath()` and `Options.GetValidAbsPath()` so JS `require()`- based module loads and helper file resolution use the same rules. Signed-off-by: Dwi Siswanto <git@dw1.io> --------- Signed-off-by: Dwi Siswanto <git@dw1.io>
78 lines
2.3 KiB
Go
78 lines
2.3 KiB
Go
package protocolstate
|
|
|
|
import (
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
|
filepathutil "github.com/projectdiscovery/nuclei/v3/pkg/utils/filepath"
|
|
"github.com/projectdiscovery/utils/errkit"
|
|
fileutil "github.com/projectdiscovery/utils/file"
|
|
mapsutil "github.com/projectdiscovery/utils/maps"
|
|
)
|
|
|
|
var (
|
|
// LfaAllowed means local file access is allowed
|
|
LfaAllowed *mapsutil.SyncLockMap[string, bool]
|
|
)
|
|
|
|
func init() {
|
|
LfaAllowed = mapsutil.NewSyncLockMap[string, bool]()
|
|
}
|
|
|
|
// IsLfaAllowed returns whether local file access is allowed
|
|
func IsLfaAllowed(options *types.Options) bool {
|
|
if GetLfaAllowed(options) {
|
|
return true
|
|
}
|
|
|
|
// Otherwise look into dialers
|
|
dialers, ok := dialers.Get(options.ExecutionId)
|
|
if ok && dialers != nil {
|
|
dialers.Lock()
|
|
defer dialers.Unlock()
|
|
|
|
return dialers.LocalFileAccessAllowed
|
|
}
|
|
|
|
// otherwise just return option value
|
|
return options.AllowLocalFileAccess
|
|
}
|
|
|
|
func SetLfaAllowed(options *types.Options) {
|
|
_ = LfaAllowed.Set(options.ExecutionId, options.AllowLocalFileAccess)
|
|
}
|
|
|
|
func GetLfaAllowed(options *types.Options) bool {
|
|
allowed, ok := LfaAllowed.Get(options.ExecutionId)
|
|
|
|
return ok && allowed
|
|
}
|
|
|
|
func NormalizePathWithExecutionId(executionId string, filePath string) (string, error) {
|
|
options := &types.Options{
|
|
ExecutionId: executionId,
|
|
}
|
|
return NormalizePath(options, filePath)
|
|
}
|
|
|
|
// Normalizepath normalizes path and returns absolute path
|
|
// it returns error if path is not allowed
|
|
// this respects the sandbox rules and only loads files from
|
|
// allowed directories
|
|
func NormalizePath(options *types.Options, filePath string) (string, error) {
|
|
// TODO: this should be tied to executionID using *types.Options
|
|
if IsLfaAllowed(options) {
|
|
// if local file access is allowed, we can return the absolute path
|
|
return filePath, nil
|
|
}
|
|
cleaned, err := fileutil.ResolveNClean(filePath, config.DefaultConfig.GetTemplateDir())
|
|
if err != nil {
|
|
return "", errkit.Wrapf(err, "could not resolve and clean path %v", filePath)
|
|
}
|
|
// only allow files inside nuclei-templates directory
|
|
// even current working directory is not allowed
|
|
if filepathutil.IsPathWithinDirectory(cleaned, config.DefaultConfig.GetTemplateDir()) {
|
|
return cleaned, nil
|
|
}
|
|
return "", errkit.Newf("path %v is outside nuclei-template directory and -lfa is not enabled", filePath)
|
|
}
|