Helm is a tool that streamlines installing and managing Kubernetes applications.`getHostByName` is a Helm template function introduced in Helm v3. The function is able to accept a hostname and return an IP address for that hostname. To get the IP address the function performs a DNS lookup. The DNS lookup happens when used with `helm install|upgrade|template` or when the Helm SDK is used to render a chart. Information passed into the chart can be disclosed to the DNS servers used to lookup the IP address. For example, a malicious chart could inject `getHostByName` into a chart in order to disclose values to a malicious DNS server. The issue has been fixed in Helm 3.11.1. Prior to using a chart with Helm verify the `getHostByName` function is not being used in a template to disclose any information you do not want passed to DNS servers.

Exposure of Sensitive Information to an Unauthorized Actor

The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.

type Engine struct {
	// If strict is enabled, template rendering will fail if a template references
	// a value that was not passed in.
	Strict bool
	// In LintMode, some 'required' template values may be missing, so don't fail
	LintMode bool
	// the rest config to connect to the kubernetes api
	config *rest.Config
}

func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
	funcMap := funcMap()
	includedNames := make(map[string]int)

	// Add the 'include' function here so we can close over t.
	funcMap["include"] = func(name string, data interface{}) (string, error) {
		var buf strings.Builder
		if v, ok := includedNames[name]; ok {
			if v > recursionMaxNums {
				return "", errors.Wrapf(fmt.Errorf("unable to execute template"), "rendering template has a nested reference name: %s", name)
			}
			includedNames[name]++
		} else {
			includedNames[name] = 1
		}
		err := t.ExecuteTemplate(&buf, name, data)
		includedNames[name]--
		return buf.String(), err
	}

	// Add the 'tpl' function here
	funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) {
		basePath, err := vals.PathValue("Template.BasePath")
		if err != nil {
			return "", errors.Wrapf(err, "cannot retrieve Template.Basepath from values inside tpl function: %s", tpl)
		}

		templateName, err := vals.PathValue("Template.Name")
		if err != nil {
			return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl)
		}

		templates := map[string]renderable{
			templateName.(string): {
				tpl:      tpl,
				vals:     vals,
				basePath: basePath.(string),
			},
		}

		result, err := e.renderWithReferences(templates, referenceTpls)
		if err != nil {
			return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl)
		}
		return result[templateName.(string)], nil
	}

	// Add the `required` function here so we can use lintMode
	funcMap["required"] = func(warn string, val interface{}) (interface{}, error) {
		if val == nil {
			if e.LintMode {
				// Don't fail on missing required values when linting
				log.Printf("[INFO] Missing required value: %s", warn)
				return "", nil
			}
			return val, errors.Errorf(warnWrap(warn))
		} else if _, ok := val.(string); ok {
			if val == "" {
				if e.LintMode {
					// Don't fail on missing required values when linting
					log.Printf("[INFO] Missing required value: %s", warn)
					return "", nil
				}
				return val, errors.Errorf(warnWrap(warn))
			}
		}
		return val, nil
	}

	// Override sprig fail function for linting and wrapping message
	funcMap["fail"] = func(msg string) (string, error) {
		if e.LintMode {
			// Don't fail when linting
			log.Printf("[INFO] Fail: %s", msg)
			return "", nil
		}
		return "", errors.New(warnWrap(msg))
	}

	// If we are not linting and have a cluster connection, provide a Kubernetes-backed
	// implementation.
	if !e.LintMode && e.config != nil {
		funcMap["lookup"] = NewLookupFunction(e.config)
	}

	t.Funcs(funcMap)
}

type Engine struct {
	// If strict is enabled, template rendering will fail if a template references
	// a value that was not passed in.
	Strict bool
	// In LintMode, some 'required' template values may be missing, so don't fail
	LintMode bool
	// the rest config to connect to the kubernetes api
	config *rest.Config
	// EnableDNS tells the engine to allow DNS lookups when rendering templates
	EnableDNS bool
}

// New creates a new instance of Engine using the passed in rest config.
func New(config *rest.Config) Engine {
	return Engine{
		config: config,
	}
}

func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
	funcMap := funcMap()
	includedNames := make(map[string]int)

	// Add the 'include' function here so we can close over t.
	funcMap["include"] = func(name string, data interface{}) (string, error) {
		var buf strings.Builder
		if v, ok := includedNames[name]; ok {
			if v > recursionMaxNums {
				return "", errors.Wrapf(fmt.Errorf("unable to execute template"), "rendering template has a nested reference name: %s", name)
			}
			includedNames[name]++
		} else {
			includedNames[name] = 1
		}
		err := t.ExecuteTemplate(&buf, name, data)
		includedNames[name]--
		return buf.String(), err
	}

	// Add the 'tpl' function here
	funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) {
		basePath, err := vals.PathValue("Template.BasePath")
		if err != nil {
			return "", errors.Wrapf(err, "cannot retrieve Template.Basepath from values inside tpl function: %s", tpl)
		}

		templateName, err := vals.PathValue("Template.Name")
		if err != nil {
			return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl)
		}

		templates := map[string]renderable{
			templateName.(string): {
				tpl:      tpl,
				vals:     vals,
				basePath: basePath.(string),
			},
		}

		result, err := e.renderWithReferences(templates, referenceTpls)
		if err != nil {
			return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl)
		}
		return result[templateName.(string)], nil
	}

	// Add the `required` function here so we can use lintMode
	funcMap["required"] = func(warn string, val interface{}) (interface{}, error) {
		if val == nil {
			if e.LintMode {
				// Don't fail on missing required values when linting
				log.Printf("[INFO] Missing required value: %s", warn)
				return "", nil
			}
			return val, errors.Errorf(warnWrap(warn))
		} else if _, ok := val.(string); ok {
			if val == "" {
				if e.LintMode {
					// Don't fail on missing required values when linting
					log.Printf("[INFO] Missing required value: %s", warn)
					return "", nil
				}
				return val, errors.Errorf(warnWrap(warn))
			}
		}
		return val, nil
	}

	// Override sprig fail function for linting and wrapping message
	funcMap["fail"] = func(msg string) (string, error) {
		if e.LintMode {
			// Don't fail when linting
			log.Printf("[INFO] Fail: %s", msg)
			return "", nil
		}
		return "", errors.New(warnWrap(msg))
	}

	// If we are not linting and have a cluster connection, provide a Kubernetes-backed
	// implementation.
	if !e.LintMode && e.config != nil {
		funcMap["lookup"] = NewLookupFunction(e.config)
	}

	// When DNS lookups are not enabled override the sprig function and return
	// an empty string.
	if !e.EnableDNS {
		funcMap["getHostByName"] = func(name string) string {
			return ""
		}
	}

	t.Funcs(funcMap)
}

type Upgrade struct {
	cfg *Configuration

	ChartPathOptions

	// Install is a purely informative flag that indicates whether this upgrade was done in "install" mode.
	//
	// Applications may use this to determine whether this Upgrade operation was done as part of a
	// pure upgrade (Upgrade.Install == false) or as part of an install-or-upgrade operation
	// (Upgrade.Install == true).
	//
	// Setting this to `true` will NOT cause `Upgrade` to perform an install if the release does not exist.
	// That process must be handled by creating an Install action directly. See cmd/upgrade.go for an
	// example of how this flag is used.
	Install bool
	// Devel indicates that the operation is done in devel mode.
	Devel bool
	// Namespace is the namespace in which this operation should be performed.
	Namespace string
	// SkipCRDs skips installing CRDs when install flag is enabled during upgrade
	SkipCRDs bool
	// Timeout is the timeout for this operation
	Timeout time.Duration
	// Wait determines whether the wait operation should be performed after the upgrade is requested.
	Wait bool
	// WaitForJobs determines whether the wait operation for the Jobs should be performed after the upgrade is requested.
	WaitForJobs bool
	// DisableHooks disables hook processing if set to true.
	DisableHooks bool
	// DryRun controls whether the operation is prepared, but not executed.
	// If `true`, the upgrade is prepared but not performed.
	DryRun bool
	// Force will, if set to `true`, ignore certain warnings and perform the upgrade anyway.
	//
	// This should be used with caution.
	Force bool
	// ResetValues will reset the values to the chart's built-ins rather than merging with existing.
	ResetValues bool
	// ReuseValues will re-use the user's last supplied values.
	ReuseValues bool
	// Recreate will (if true) recreate pods after a rollback.
	Recreate bool
	// MaxHistory limits the maximum number of revisions saved per release
	MaxHistory int
	// Atomic, if true, will roll back on failure.
	Atomic bool
	// CleanupOnFail will, if true, cause the upgrade to delete newly-created resources on a failed update.
	CleanupOnFail bool
	// SubNotes determines whether sub-notes are rendered in the chart.
	SubNotes bool
	// Description is the description of this operation
	Description string
	// PostRender is an optional post-renderer
	//
	// If this is non-nil, then after templates are rendered, they will be sent to the
	// post renderer before sending to the Kubernetes API server.
	PostRenderer postrender.PostRenderer
	// DisableOpenAPIValidation controls whether OpenAPI validation is enforced.
	DisableOpenAPIValidation bool
	// Get missing dependencies
	DependencyUpdate bool
	// Lock to control raceconditions when the process receives a SIGTERM
	Lock sync.Mutex
	// Enable DNS lookups when rendering templates
	EnableDNS bool
}

func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, *release.Release, error) {
	if chart == nil {
		return nil, nil, errMissingChart
	}

	// finds the last non-deleted release with the given name
	lastRelease, err := u.cfg.Releases.Last(name)
	if err != nil {
		// to keep existing behavior of returning the "%q has no deployed releases" error when an existing release does not exist
		if errors.Is(err, driver.ErrReleaseNotFound) {
			return nil, nil, driver.NewErrNoDeployedReleases(name)
		}
		return nil, nil, err
	}

	// Concurrent `helm upgrade`s will either fail here with `errPending` or when creating the release with "already exists". This should act as a pessimistic lock.
	if lastRelease.Info.Status.IsPending() {
		return nil, nil, errPending
	}

	var currentRelease *release.Release
	if lastRelease.Info.Status == release.StatusDeployed {
		// no need to retrieve the last deployed release from storage as the last release is deployed
		currentRelease = lastRelease
	} else {
		// finds the deployed release with the given name
		currentRelease, err = u.cfg.Releases.Deployed(name)
		if err != nil {
			if errors.Is(err, driver.ErrNoDeployedReleases) &&
				(lastRelease.Info.Status == release.StatusFailed || lastRelease.Info.Status == release.StatusSuperseded) {
				currentRelease = lastRelease
			} else {
				return nil, nil, err
			}
		}
	}

	// determine if values will be reused
	vals, err = u.reuseValues(chart, currentRelease, vals)
	if err != nil {
		return nil, nil, err
	}

	if err := chartutil.ProcessDependencies(chart, vals); err != nil {
		return nil, nil, err
	}

	// Increment revision count. This is passed to templates, and also stored on
	// the release object.
	revision := lastRelease.Version + 1

	options := chartutil.ReleaseOptions{
		Name:      name,
		Namespace: currentRelease.Namespace,
		Revision:  revision,
		IsUpgrade: true,
	}

	caps, err := u.cfg.getCapabilities()
	if err != nil {
		return nil, nil, err
	}
	valuesToRender, err := chartutil.ToRenderValues(chart, vals, options, caps)
	if err != nil {
		return nil, nil, err
	}

	hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun, u.EnableDNS)
	if err != nil {
		return nil, nil, err
	}

	// Store an upgraded release.
	upgradedRelease := &release.Release{
		Name:      name,
		Namespace: currentRelease.Namespace,
		Chart:     chart,
		Config:    vals,
		Info: &release.Info{
			FirstDeployed: currentRelease.Info.FirstDeployed,
			LastDeployed:  Timestamper(),
			Status:        release.StatusPendingUpgrade,
			Description:   "Preparing upgrade", // This should be overwritten later.
		},
		Version:  revision,
		Manifest: manifestDoc.String(),
		Hooks:    hooks,
	}

	if len(notesTxt) > 0 {
		upgradedRelease.Info.Notes = notesTxt
	}
	err = validateManifest(u.cfg.KubeClient, manifestDoc.Bytes(), !u.DisableOpenAPIValidation)
	return currentRelease, upgradedRelease, err
}

type Install struct {
	cfg *Configuration

	ChartPathOptions

	ClientOnly               bool
	Force                    bool
	CreateNamespace          bool
	DryRun                   bool
	DisableHooks             bool
	Replace                  bool
	Wait                     bool
	WaitForJobs              bool
	Devel                    bool
	DependencyUpdate         bool
	Timeout                  time.Duration
	Namespace                string
	ReleaseName              string
	GenerateName             bool
	NameTemplate             string
	Description              string
	OutputDir                string
	Atomic                   bool
	SkipCRDs                 bool
	SubNotes                 bool
	DisableOpenAPIValidation bool
	IncludeCRDs              bool
	// KubeVersion allows specifying a custom kubernetes version to use and
	// APIVersions allows a manual set of supported API Versions to be passed
	// (for things like templating). These are ignored if ClientOnly is false
	KubeVersion *chartutil.KubeVersion
	APIVersions chartutil.VersionSet
	// Used by helm template to render charts with .Release.IsUpgrade. Ignored if Dry-Run is false
	IsUpgrade bool
	// Enable DNS lookups when rendering templates
	EnableDNS bool
	// Used by helm template to add the release as part of OutputDir path
	// OutputDir/<ReleaseName>
	UseReleaseName bool
	PostRenderer   postrender.PostRenderer
	// Lock to control raceconditions when the process receives a SIGTERM
	Lock sync.Mutex
}

func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
	// Check reachability of cluster unless in client-only mode (e.g. `helm template` without `--validate`)
	if !i.ClientOnly {
		if err := i.cfg.KubeClient.IsReachable(); err != nil {
			return nil, err
		}
	}

	if err := i.availableName(); err != nil {
		return nil, err
	}

	if err := chartutil.ProcessDependencies(chrt, vals); err != nil {
		return nil, err
	}

	// Pre-install anything in the crd/ directory. We do this before Helm
	// contacts the upstream server and builds the capabilities object.
	if crds := chrt.CRDObjects(); !i.ClientOnly && !i.SkipCRDs && len(crds) > 0 {
		// On dry run, bail here
		if i.DryRun {
			i.cfg.Log("WARNING: This chart or one of its subcharts contains CRDs. Rendering may fail or contain inaccuracies.")
		} else if err := i.installCRDs(crds); err != nil {
			return nil, err
		}
	}

	if i.ClientOnly {
		// Add mock objects in here so it doesn't use Kube API server
		// NOTE(bacongobbler): used for `helm template`
		i.cfg.Capabilities = chartutil.DefaultCapabilities.Copy()
		if i.KubeVersion != nil {
			i.cfg.Capabilities.KubeVersion = *i.KubeVersion
		}
		i.cfg.Capabilities.APIVersions = append(i.cfg.Capabilities.APIVersions, i.APIVersions...)
		i.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard}

		mem := driver.NewMemory()
		mem.SetNamespace(i.Namespace)
		i.cfg.Releases = storage.Init(mem)
	} else if !i.ClientOnly && len(i.APIVersions) > 0 {
		i.cfg.Log("API Version list given outside of client only mode, this list will be ignored")
	}

	// Make sure if Atomic is set, that wait is set as well. This makes it so
	// the user doesn't have to specify both
	i.Wait = i.Wait || i.Atomic

	caps, err := i.cfg.getCapabilities()
	if err != nil {
		return nil, err
	}

	// special case for helm template --is-upgrade
	isUpgrade := i.IsUpgrade && i.DryRun
	options := chartutil.ReleaseOptions{
		Name:      i.ReleaseName,
		Namespace: i.Namespace,
		Revision:  1,
		IsInstall: !isUpgrade,
		IsUpgrade: isUpgrade,
	}
	valuesToRender, err := chartutil.ToRenderValues(chrt, vals, options, caps)
	if err != nil {
		return nil, err
	}

	rel := i.createRelease(chrt, vals)

	var manifestDoc *bytes.Buffer
	rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun, i.EnableDNS)
	// Even for errors, attach this if available
	if manifestDoc != nil {
		rel.Manifest = manifestDoc.String()
	}
	// Check error from render
	if err != nil {
		rel.SetStatus(release.StatusFailed, fmt.Sprintf("failed to render resource: %s", err.Error()))
		// Return a release with partial data so that the client can show debugging information.
		return rel, err
	}

	// Mark this release as in-progress
	rel.SetStatus(release.StatusPendingInstall, "Initial install underway")

	var toBeAdopted kube.ResourceList
	resources, err := i.cfg.KubeClient.Build(bytes.NewBufferString(rel.Manifest), !i.DisableOpenAPIValidation)
	if err != nil {
		return nil, errors.Wrap(err, "unable to build kubernetes objects from release manifest")
	}

	// It is safe to use "force" here because these are resources currently rendered by the chart.
	err = resources.Visit(setMetadataVisitor(rel.Name, rel.Namespace, true))
	if err != nil {
		return nil, err
	}

	// Install requires an extra validation step of checking that resources
	// don't already exist before we actually create resources. If we continue
	// forward and create the release object with resources that already exist,
	// we'll end up in a state where we will delete those resources upon
	// deleting the release because the manifest will be pointing at that
	// resource
	if !i.ClientOnly && !isUpgrade && len(resources) > 0 {
		toBeAdopted, err = existingResourceConflict(resources, rel.Name, rel.Namespace)
		if err != nil {
			return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install")
		}
	}

	// Bail out here if it is a dry run
	if i.DryRun {
		rel.Info.Description = "Dry run complete"
		return rel, nil
	}

	if i.CreateNamespace {
		ns := &v1.Namespace{
			TypeMeta: metav1.TypeMeta{
				APIVersion: "v1",
				Kind:       "Namespace",
			},
			ObjectMeta: metav1.ObjectMeta{
				Name: i.Namespace,
				Labels: map[string]string{
					"name": i.Namespace,
				},
			},
		}
		buf, err := yaml.Marshal(ns)
		if err != nil {
			return nil, err
		}
		resourceList, err := i.cfg.KubeClient.Build(bytes.NewBuffer(buf), true)
		if err != nil {
			return nil, err
		}
		if _, err := i.cfg.KubeClient.Create(resourceList); err != nil && !apierrors.IsAlreadyExists(err) {
			return nil, err
		}
	}

	// If Replace is true, we need to supercede the last release.
	if i.Replace {
		if err := i.replaceRelease(rel); err != nil {
			return nil, err
		}
	}

	// Store the release in history before continuing (new in Helm 3). We always know
	// that this is a create operation.
	if err := i.cfg.Releases.Create(rel); err != nil {
		// We could try to recover gracefully here, but since nothing has been installed
		// yet, this is probably safer than trying to continue when we know storage is
		// not working.
		return rel, err
	}
	rChan := make(chan resultMessage)
	doneChan := make(chan struct{})
	defer close(doneChan)
	go i.performInstall(rChan, rel, toBeAdopted, resources)
	go i.handleContext(ctx, rChan, doneChan, rel)
	result := <-rChan
	//start preformInstall go routine
	return result.r, result.e
}

func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun, enableDNS bool) ([]*release.Hook, *bytes.Buffer, string, error) {
	hs := []*release.Hook{}
	b := bytes.NewBuffer(nil)

	caps, err := cfg.getCapabilities()
	if err != nil {
		return hs, b, "", err
	}

	if ch.Metadata.KubeVersion != "" {
		if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) {
			return hs, b, "", errors.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String())
		}
	}

	var files map[string]string
	var err2 error

	// A `helm template` or `helm install --dry-run` should not talk to the remote cluster.
	// It will break in interesting and exotic ways because other data (e.g. discovery)
	// is mocked. It is not up to the template author to decide when the user wants to
	// connect to the cluster. So when the user says to dry run, respect the user's
	// wishes and do not connect to the cluster.
	if !dryRun && cfg.RESTClientGetter != nil {
		restConfig, err := cfg.RESTClientGetter.ToRESTConfig()
		if err != nil {
			return hs, b, "", err
		}
		e := engine.New(restConfig)
		e.EnableDNS = enableDNS
		files, err2 = e.Render(ch, values)
	} else {
		var e engine.Engine
		e.EnableDNS = enableDNS
		files, err2 = e.Render(ch, values)
	}

	if err2 != nil {
		return hs, b, "", err2
	}

	// NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource,
	// pull it out of here into a separate file so that we can actually use the output of the rendered
	// text file. We have to spin through this map because the file contains path information, so we
	// look for terminating NOTES.txt. We also remove it from the files so that we don't have to skip
	// it in the sortHooks.
	var notesBuffer bytes.Buffer
	for k, v := range files {
		if strings.HasSuffix(k, notesFileSuffix) {
			if subNotes || (k == path.Join(ch.Name(), "templates", notesFileSuffix)) {
				// If buffer contains data, add newline before adding more
				if notesBuffer.Len() > 0 {
					notesBuffer.WriteString("\n")
				}
				notesBuffer.WriteString(v)
			}
			delete(files, k)
		}
	}
	notes := notesBuffer.String()

	// Sort hooks, manifests, and partials. Only hooks and manifests are returned,
	// as partials are not used after renderer.Render. Empty manifests are also
	// removed here.
	hs, manifests, err := releaseutil.SortManifests(files, caps.APIVersions, releaseutil.InstallOrder)
	if err != nil {
		// By catching parse errors here, we can prevent bogus releases from going
		// to Kubernetes.
		//
		// We return the files as a big blob of data to help the user debug parser
		// errors.
		for name, content := range files {
			if strings.TrimSpace(content) == "" {
				continue
			}
			fmt.Fprintf(b, "---\n# Source: %s\n%s\n", name, content)
		}
		return hs, b, "", err
	}

	// Aggregate all valid manifests into one big doc.
	fileWritten := make(map[string]bool)

	if includeCrds {
		for _, crd := range ch.CRDObjects() {
			if outputDir == "" {
				fmt.Fprintf(b, "---\n# Source: %s\n%s\n", crd.Name, string(crd.File.Data[:]))
			} else {
				err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Name])
				if err != nil {
					return hs, b, "", err
				}
				fileWritten[crd.Name] = true
			}
		}
	}

	for _, m := range manifests {
		if outputDir == "" {
			fmt.Fprintf(b, "---\n# Source: %s\n%s\n", m.Name, m.Content)
		} else {
			newDir := outputDir
			if useReleaseName {
				newDir = filepath.Join(outputDir, releaseName)
			}
			// NOTE: We do not have to worry about the post-renderer because
			// output dir is only used by `helm template`. In the next major
			// release, we should move this logic to template only as it is not
			// used by install or upgrade
			err = writeToFile(newDir, m.Name, m.Content, fileWritten[m.Name])
			if err != nil {
				return hs, b, "", err
			}
			fileWritten[m.Name] = true
		}
	}

	if pr != nil {
		b, err = pr.Run(b)
		if err != nil {
			return hs, b, notes, errors.Wrap(err, "error while running post render on files")
		}
	}

	return hs, b, notes, nil
}

func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
	client := action.NewUpgrade(cfg)
	valueOpts := &values.Options{}
	var outfmt output.Format
	var createNamespace bool

	cmd := &cobra.Command{
		Use:   "upgrade [RELEASE] [CHART]",
		Short: "upgrade a release",
		Long:  upgradeDesc,
		Args:  require.ExactArgs(2),
		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
			if len(args) == 0 {
				return compListReleases(toComplete, args, cfg)
			}
			if len(args) == 1 {
				return compListCharts(toComplete, true)
			}
			return nil, cobra.ShellCompDirectiveNoFileComp
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			client.Namespace = settings.Namespace()

			// Fixes #7002 - Support reading values from STDIN for `upgrade` command
			// Must load values AFTER determining if we have to call install so that values loaded from stdin are are not read twice
			if client.Install {
				// If a release does not exist, install it.
				histClient := action.NewHistory(cfg)
				histClient.Max = 1
				if _, err := histClient.Run(args[0]); err == driver.ErrReleaseNotFound {
					// Only print this to stdout for table output
					if outfmt == output.Table {
						fmt.Fprintf(out, "Release %q does not exist. Installing it now.\n", args[0])
					}
					instClient := action.NewInstall(cfg)
					instClient.CreateNamespace = createNamespace
					instClient.ChartPathOptions = client.ChartPathOptions
					instClient.Force = client.Force
					instClient.DryRun = client.DryRun
					instClient.DisableHooks = client.DisableHooks
					instClient.SkipCRDs = client.SkipCRDs
					instClient.Timeout = client.Timeout
					instClient.Wait = client.Wait
					instClient.WaitForJobs = client.WaitForJobs
					instClient.Devel = client.Devel
					instClient.Namespace = client.Namespace
					instClient.Atomic = client.Atomic
					instClient.PostRenderer = client.PostRenderer
					instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation
					instClient.SubNotes = client.SubNotes
					instClient.Description = client.Description
					instClient.DependencyUpdate = client.DependencyUpdate
					instClient.EnableDNS = client.EnableDNS

					rel, err := runInstall(args, instClient, valueOpts, out)
					if err != nil {
						return err
					}
					return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false})
				} else if err != nil {
					return err
				}
			}

			if client.Version == "" && client.Devel {
				debug("setting version to >0.0.0-0")
				client.Version = ">0.0.0-0"
			}

			chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings)
			if err != nil {
				return err
			}

			p := getter.All(settings)
			vals, err := valueOpts.MergeValues(p)
			if err != nil {
				return err
			}

			// Check chart dependencies to make sure all are present in /charts
			ch, err := loader.Load(chartPath)
			if err != nil {
				return err
			}
			if req := ch.Metadata.Dependencies; req != nil {
				if err := action.CheckDependencies(ch, req); err != nil {
					err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies")
					if client.DependencyUpdate {
						man := &downloader.Manager{
							Out:              out,
							ChartPath:        chartPath,
							Keyring:          client.ChartPathOptions.Keyring,
							SkipUpdate:       false,
							Getters:          p,
							RepositoryConfig: settings.RepositoryConfig,
							RepositoryCache:  settings.RepositoryCache,
							Debug:            settings.Debug,
						}
						if err := man.Update(); err != nil {
							return err
						}
						// Reload the chart with the updated Chart.lock file.
						if ch, err = loader.Load(chartPath); err != nil {
							return errors.Wrap(err, "failed reloading chart after repo update")
						}
					} else {
						return err
					}
				}
			}

			if ch.Metadata.Deprecated {
				warning("This chart is deprecated")
			}

			// Create context and prepare the handle of SIGTERM
			ctx := context.Background()
			ctx, cancel := context.WithCancel(ctx)

			// Set up channel on which to send signal notifications.
			// We must use a buffered channel or risk missing the signal
			// if we're not ready to receive when the signal is sent.
			cSignal := make(chan os.Signal, 2)
			signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
			go func() {
				<-cSignal
				fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0])
				cancel()
			}()

			rel, err := client.RunWithContext(ctx, args[0], ch, vals)
			if err != nil {
				return errors.Wrap(err, "UPGRADE FAILED")
			}

			if outfmt == output.Table {
				fmt.Fprintf(out, "Release %q has been upgraded. Happy Helming!\n", args[0])
			}

			return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false})
		},
	}

	f := cmd.Flags()
	f.BoolVar(&createNamespace, "create-namespace", false, "if --install is set, create the release namespace if not present")
	f.BoolVarP(&client.Install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
	f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
	f.BoolVar(&client.DryRun, "dry-run", false, "simulate an upgrade")
	f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
	f.MarkDeprecated("recreate-pods", "functionality will no longer be updated. Consult the documentation for other methods to recreate pods")
	f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
	f.BoolVar(&client.DisableHooks, "no-hooks", false, "disable pre/post upgrade hooks")
	f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the upgrade process will not validate rendered templates against the Kubernetes OpenAPI Schema")
	f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed when an upgrade is performed with install flag enabled. By default, CRDs are installed if not already present, when an upgrade is performed with install flag enabled")
	f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
	f.BoolVar(&client.ResetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
	f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored")
	f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")
	f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
	f.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically if --atomic is used")
	f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit")
	f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails")
	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
	f.StringVar(&client.Description, "description", "", "add a custom description")
	f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
	f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
	addChartPathOptionsFlags(f, &client.ChartPathOptions)
	addValueOptionsFlags(f, valueOpts)
	bindOutputFlag(cmd, &outfmt)
	bindPostRenderFlag(cmd, &client.PostRenderer)

	err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
		if len(args) != 2 {
			return nil, cobra.ShellCompDirectiveNoFileComp
		}
		return compVersionFlag(args[1], toComplete)
	})

	if err != nil {
		log.Fatal(err)
	}

	return cmd
}

func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) {
	f.BoolVar(&client.CreateNamespace, "create-namespace", false, "create the release namespace if not present")
	f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install")
	f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
	f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
	f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
	f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
	f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")
	f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
	f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)")
	f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release")
	f.StringVar(&client.Description, "description", "", "add a custom description")
	f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
	f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
	f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema")
	f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
	f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
	f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
	addValueOptionsFlags(f, valueOpts)
	addChartPathOptionsFlags(f, &client.ChartPathOptions)

	err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
		requiredArgs := 2
		if client.GenerateName {
			requiredArgs = 1
		}
		if len(args) != requiredArgs {
			return nil, cobra.ShellCompDirectiveNoFileComp
		}
		return compVersionFlag(args[requiredArgs-1], toComplete)
	})

	if err != nil {
		log.Fatal(err)
	}
}

--- a/pkg/engine/engine.go
+++ b/pkg/engine/engine.go
@@ -6,4 +6,13 @@
 	LintMode bool
 	// the rest config to connect to the kubernetes api
 	config *rest.Config
+	// EnableDNS tells the engine to allow DNS lookups when rendering templates
+	EnableDNS bool
 }
+
+// New creates a new instance of Engine using the passed in rest config.
+func New(config *rest.Config) Engine {
+	return Engine{
+		config: config,
+	}
+}

--- a/pkg/engine/engine.go
+++ b/pkg/engine/engine.go
@@ -83,5 +83,13 @@
 		funcMap["lookup"] = NewLookupFunction(e.config)
 	}

+	// When DNS lookups are not enabled override the sprig function and return
+	// an empty string.
+	if !e.EnableDNS {
+		funcMap["getHostByName"] = func(name string) string {
+			return ""
+		}
+	}
+
 	t.Funcs(funcMap)
 }

--- /dev/null
+++ b/pkg/engine/engine.go
@@ -0,0 +1,66 @@
+type Upgrade struct {
+	cfg *Configuration
+
+	ChartPathOptions
+
+	// Install is a purely informative flag that indicates whether this upgrade was done in "install" mode.
+	//
+	// Applications may use this to determine whether this Upgrade operation was done as part of a
+	// pure upgrade (Upgrade.Install == false) or as part of an install-or-upgrade operation
+	// (Upgrade.Install == true).
+	//
+	// Setting this to `true` will NOT cause `Upgrade` to perform an install if the release does not exist.
+	// That process must be handled by creating an Install action directly. See cmd/upgrade.go for an
+	// example of how this flag is used.
+	Install bool
+	// Devel indicates that the operation is done in devel mode.
+	Devel bool
+	// Namespace is the namespace in which this operation should be performed.
+	Namespace string
+	// SkipCRDs skips installing CRDs when install flag is enabled during upgrade
+	SkipCRDs bool
+	// Timeout is the timeout for this operation
+	Timeout time.Duration
+	// Wait determines whether the wait operation should be performed after the upgrade is requested.
+	Wait bool
+	// WaitForJobs determines whether the wait operation for the Jobs should be performed after the upgrade is requested.
+	WaitForJobs bool
+	// DisableHooks disables hook processing if set to true.
+	DisableHooks bool
+	// DryRun controls whether the operation is prepared, but not executed.
+	// If `true`, the upgrade is prepared but not performed.
+	DryRun bool
+	// Force will, if set to `true`, ignore certain warnings and perform the upgrade anyway.
+	//
+	// This should be used with caution.
+	Force bool
+	// ResetValues will reset the values to the chart's built-ins rather than merging with existing.
+	ResetValues bool
+	// ReuseValues will re-use the user's last supplied values.
+	ReuseValues bool
+	// Recreate will (if true) recreate pods after a rollback.
+	Recreate bool
+	// MaxHistory limits the maximum number of revisions saved per release
+	MaxHistory int
+	// Atomic, if true, will roll back on failure.
+	Atomic bool
+	// CleanupOnFail will, if true, cause the upgrade to delete newly-created resources on a failed update.
+	CleanupOnFail bool
+	// SubNotes determines whether sub-notes are rendered in the chart.
+	SubNotes bool
+	// Description is the description of this operation
+	Description string
+	// PostRender is an optional post-renderer
+	//
+	// If this is non-nil, then after templates are rendered, they will be sent to the
+	// post renderer before sending to the Kubernetes API server.
+	PostRenderer postrender.PostRenderer
+	// DisableOpenAPIValidation controls whether OpenAPI validation is enforced.
+	DisableOpenAPIValidation bool
+	// Get missing dependencies
+	DependencyUpdate bool
+	// Lock to control raceconditions when the process receives a SIGTERM
+	Lock sync.Mutex
+	// Enable DNS lookups when rendering templates
+	EnableDNS bool
+}

--- /dev/null
+++ b/pkg/engine/engine.go
@@ -0,0 +1,95 @@
+func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, *release.Release, error) {
+	if chart == nil {
+		return nil, nil, errMissingChart
+	}
+
+	// finds the last non-deleted release with the given name
+	lastRelease, err := u.cfg.Releases.Last(name)
+	if err != nil {
+		// to keep existing behavior of returning the "%q has no deployed releases" error when an existing release does not exist
+		if errors.Is(err, driver.ErrReleaseNotFound) {
+			return nil, nil, driver.NewErrNoDeployedReleases(name)
+		}
+		return nil, nil, err
+	}
+
+	// Concurrent `helm upgrade`s will either fail here with `errPending` or when creating the release with "already exists". This should act as a pessimistic lock.
+	if lastRelease.Info.Status.IsPending() {
+		return nil, nil, errPending
+	}
+
+	var currentRelease *release.Release
+	if lastRelease.Info.Status == release.StatusDeployed {
+		// no need to retrieve the last deployed release from storage as the last release is deployed
+		currentRelease = lastRelease
+	} else {
+		// finds the deployed release with the given name
+		currentRelease, err = u.cfg.Releases.Deployed(name)
+		if err != nil {
+			if errors.Is(err, driver.ErrNoDeployedReleases) &&
+				(lastRelease.Info.Status == release.StatusFailed || lastRelease.Info.Status == release.StatusSuperseded) {
+				currentRelease = lastRelease
+			} else {
+				return nil, nil, err
+			}
+		}
+	}
+
+	// determine if values will be reused
+	vals, err = u.reuseValues(chart, currentRelease, vals)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	if err := chartutil.ProcessDependencies(chart, vals); err != nil {
+		return nil, nil, err
+	}
+
+	// Increment revision count. This is passed to templates, and also stored on
+	// the release object.
+	revision := lastRelease.Version + 1
+
+	options := chartutil.ReleaseOptions{
+		Name:      name,
+		Namespace: currentRelease.Namespace,
+		Revision:  revision,
+		IsUpgrade: true,
+	}
+
+	caps, err := u.cfg.getCapabilities()
+	if err != nil {
+		return nil, nil, err
+	}
+	valuesToRender, err := chartutil.ToRenderValues(chart, vals, options, caps)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun, u.EnableDNS)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// Store an upgraded release.
+	upgradedRelease := &release.Release{
+		Name:      name,
+		Namespace: currentRelease.Namespace,
+		Chart:     chart,
+		Config:    vals,
+		Info: &release.Info{
+			FirstDeployed: currentRelease.Info.FirstDeployed,
+			LastDeployed:  Timestamper(),
+			Status:        release.StatusPendingUpgrade,
+			Description:   "Preparing upgrade", // This should be overwritten later.
+		},
+		Version:  revision,
+		Manifest: manifestDoc.String(),
+		Hooks:    hooks,
+	}
+
+	if len(notesTxt) > 0 {
+		upgradedRelease.Info.Notes = notesTxt
+	}
+	err = validateManifest(u.cfg.KubeClient, manifestDoc.Bytes(), !u.DisableOpenAPIValidation)
+	return currentRelease, upgradedRelease, err
+}

--- /dev/null
+++ b/pkg/engine/engine.go
@@ -0,0 +1,43 @@
+type Install struct {
+	cfg *Configuration
+
+	ChartPathOptions
+
+	ClientOnly               bool
+	Force                    bool
+	CreateNamespace          bool
+	DryRun                   bool
+	DisableHooks             bool
+	Replace                  bool
+	Wait                     bool
+	WaitForJobs              bool
+	Devel                    bool
+	DependencyUpdate         bool
+	Timeout                  time.Duration
+	Namespace                string
+	ReleaseName              string
+	GenerateName             bool
+	NameTemplate             string
+	Description              string
+	OutputDir                string
+	Atomic                   bool
+	SkipCRDs                 bool
+	SubNotes                 bool
+	DisableOpenAPIValidation bool
+	IncludeCRDs              bool
+	// KubeVersion allows specifying a custom kubernetes version to use and
+	// APIVersions allows a manual set of supported API Versions to be passed
+	// (for things like templating). These are ignored if ClientOnly is false
+	KubeVersion *chartutil.KubeVersion
+	APIVersions chartutil.VersionSet
+	// Used by helm template to render charts with .Release.IsUpgrade. Ignored if Dry-Run is false
+	IsUpgrade bool
+	// Enable DNS lookups when rendering templates
+	EnableDNS bool
+	// Used by helm template to add the release as part of OutputDir path
+	// OutputDir/<ReleaseName>
+	UseReleaseName bool
+	PostRenderer   postrender.PostRenderer
+	// Lock to control raceconditions when the process receives a SIGTERM
+	Lock sync.Mutex
+}

--- /dev/null
+++ b/pkg/engine/engine.go
@@ -0,0 +1,166 @@
+func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
+	// Check reachability of cluster unless in client-only mode (e.g. `helm template` without `--validate`)
+	if !i.ClientOnly {
+		if err := i.cfg.KubeClient.IsReachable(); err != nil {
+			return nil, err
+		}
+	}
+
+	if err := i.availableName(); err != nil {
+		return nil, err
+	}
+
+	if err := chartutil.ProcessDependencies(chrt, vals); err != nil {
+		return nil, err
+	}
+
+	// Pre-install anything in the crd/ directory. We do this before Helm
+	// contacts the upstream server and builds the capabilities object.
+	if crds := chrt.CRDObjects(); !i.ClientOnly && !i.SkipCRDs && len(crds) > 0 {
+		// On dry run, bail here
+		if i.DryRun {
+			i.cfg.Log("WARNING: This chart or one of its subcharts contains CRDs. Rendering may fail or contain inaccuracies.")
+		} else if err := i.installCRDs(crds); err != nil {
+			return nil, err
+		}
+	}
+
+	if i.ClientOnly {
+		// Add mock objects in here so it doesn't use Kube API server
+		// NOTE(bacongobbler): used for `helm template`
+		i.cfg.Capabilities = chartutil.DefaultCapabilities.Copy()
+		if i.KubeVersion != nil {
+			i.cfg.Capabilities.KubeVersion = *i.KubeVersion
+		}
+		i.cfg.Capabilities.APIVersions = append(i.cfg.Capabilities.APIVersions, i.APIVersions...)
+		i.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard}
+
+		mem := driver.NewMemory()
+		mem.SetNamespace(i.Namespace)
+		i.cfg.Releases = storage.Init(mem)
+	} else if !i.ClientOnly && len(i.APIVersions) > 0 {
+		i.cfg.Log("API Version list given outside of client only mode, this list will be ignored")
+	}
+
+	// Make sure if Atomic is set, that wait is set as well. This makes it so
+	// the user doesn't have to specify both
+	i.Wait = i.Wait || i.Atomic
+
+	caps, err := i.cfg.getCapabilities()
+	if err != nil {
+		return nil, err
+	}
+
+	// special case for helm template --is-upgrade
+	isUpgrade := i.IsUpgrade && i.DryRun
+	options := chartutil.ReleaseOptions{
+		Name:      i.ReleaseName,
+		Namespace: i.Namespace,
+		Revision:  1,
+		IsInstall: !isUpgrade,
+		IsUpgrade: isUpgrade,
+	}
+	valuesToRender, err := chartutil.ToRenderValues(chrt, vals, options, caps)
+	if err != nil {
+		return nil, err
+	}
+
+	rel := i.createRelease(chrt, vals)
+
+	var manifestDoc *bytes.Buffer
+	rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun, i.EnableDNS)
+	// Even for errors, attach this if available
+	if manifestDoc != nil {
+		rel.Manifest = manifestDoc.String()
+	}
+	// Check error from render
+	if err != nil {
+		rel.SetStatus(release.StatusFailed, fmt.Sprintf("failed to render resource: %s", err.Error()))
+		// Return a release with partial data so that the client can show debugging information.
+		return rel, err
+	}
+
+	// Mark this release as in-progress
+	rel.SetStatus(release.StatusPendingInstall, "Initial install underway")
+
+	var toBeAdopted kube.ResourceList
+	resources, err := i.cfg.KubeClient.Build(bytes.NewBufferString(rel.Manifest), !i.DisableOpenAPIValidation)
+	if err != nil {
+		return nil, errors.Wrap(err, "unable to build kubernetes objects from release manifest")
+	}
+
+	// It is safe to use "force" here because these are resources currently rendered by the chart.
+	err = resources.Visit(setMetadataVisitor(rel.Name, rel.Namespace, true))
+	if err != nil {
+		return nil, err
+	}
+
+	// Install requires an extra validation step of checking that resources
+	// don't already exist before we actually create resources. If we continue
+	// forward and create the release object with resources that already exist,
+	// we'll end up in a state where we will delete those resources upon
+	// deleting the release because the manifest will be pointing at that
+	// resource
+	if !i.ClientOnly && !isUpgrade && len(resources) > 0 {
+		toBeAdopted, err = existingResourceConflict(resources, rel.Name, rel.Namespace)
+		if err != nil {
+			return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install")
+		}
+	}
+
+	// Bail out here if it is a dry run
+	if i.DryRun {
+		rel.Info.Description = "Dry run complete"
+		return rel, nil
+	}
+
+	if i.CreateNamespace {
+		ns := &v1.Namespace{
+			TypeMeta: metav1.TypeMeta{
+				APIVersion: "v1",
+				Kind:       "Namespace",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name: i.Namespace,
+				Labels: map[string]string{
+					"name": i.Namespace,
+				},
+			},
+		}
+		buf, err := yaml.Marshal(ns)
+		if err != nil {
+			return nil, err
+		}
+		resourceList, err := i.cfg.KubeClient.Build(bytes.NewBuffer(buf), true)
+		if err != nil {
+			return nil, err
+		}
+		if _, err := i.cfg.KubeClient.Create(resourceList); err != nil && !apierrors.IsAlreadyExists(err) {
+			return nil, err
+		}
+	}
+
+	// If Replace is true, we need to supercede the last release.
+	if i.Replace {
+		if err := i.replaceRelease(rel); err != nil {
+			return nil, err
+		}
+	}
+
+	// Store the release in history before continuing (new in Helm 3). We always know
+	// that this is a create operation.
+	if err := i.cfg.Releases.Create(rel); err != nil {
+		// We could try to recover gracefully here, but since nothing has been installed
+		// yet, this is probably safer than trying to continue when we know storage is
+		// not working.
+		return rel, err
+	}
+	rChan := make(chan resultMessage)
+	doneChan := make(chan struct{})
+	defer close(doneChan)
+	go i.performInstall(rChan, rel, toBeAdopted, resources)
+	go i.handleContext(ctx, rChan, doneChan, rel)
+	result := <-rChan
+	//start preformInstall go routine
+	return result.r, result.e
+}

--- /dev/null
+++ b/pkg/engine/engine.go
@@ -0,0 +1,126 @@
+func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun, enableDNS bool) ([]*release.Hook, *bytes.Buffer, string, error) {
+	hs := []*release.Hook{}
+	b := bytes.NewBuffer(nil)
+
+	caps, err := cfg.getCapabilities()
+	if err != nil {
+		return hs, b, "", err
+	}
+
+	if ch.Metadata.KubeVersion != "" {
+		if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) {
+			return hs, b, "", errors.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String())
+		}
+	}
+
+	var files map[string]string
+	var err2 error
+
+	// A `helm template` or `helm install --dry-run` should not talk to the remote cluster.
+	// It will break in interesting and exotic ways because other data (e.g. discovery)
+	// is mocked. It is not up to the template author to decide when the user wants to
+	// connect to the cluster. So when the user says to dry run, respect the user's
+	// wishes and do not connect to the cluster.
+	if !dryRun && cfg.RESTClientGetter != nil {
+		restConfig, err := cfg.RESTClientGetter.ToRESTConfig()
+		if err != nil {
+			return hs, b, "", err
+		}
+		e := engine.New(restConfig)
+		e.EnableDNS = enableDNS
+		files, err2 = e.Render(ch, values)
+	} else {
+		var e engine.Engine
+		e.EnableDNS = enableDNS
+		files, err2 = e.Render(ch, values)
+	}
+
+	if err2 != nil {
+		return hs, b, "", err2
+	}
+
+	// NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource,
+	// pull it out of here into a separate file so that we can actually use the output of the rendered
+	// text file. We have to spin through this map because the file contains path information, so we
+	// look for terminating NOTES.txt. We also remove it from the files so that we don't have to skip
+	// it in the sortHooks.
+	var notesBuffer bytes.Buffer
+	for k, v := range files {
+		if strings.HasSuffix(k, notesFileSuffix) {
+			if subNotes || (k == path.Join(ch.Name(), "templates", notesFileSuffix)) {
+				// If buffer contains data, add newline before adding more
+				if notesBuffer.Len() > 0 {
+					notesBuffer.WriteString("\n")
+				}
+				notesBuffer.WriteString(v)
+			}
+			delete(files, k)
+		}
+	}
+	notes := notesBuffer.String()
+
+	// Sort hooks, manifests, and partials. Only hooks and manifests are returned,
+	// as partials are not used after renderer.Render. Empty manifests are also
+	// removed here.
+	hs, manifests, err := releaseutil.SortManifests(files, caps.APIVersions, releaseutil.InstallOrder)
+	if err != nil {
+		// By catching parse errors here, we can prevent bogus releases from going
+		// to Kubernetes.
+		//
+		// We return the files as a big blob of data to help the user debug parser
+		// errors.
+		for name, content := range files {
+			if strings.TrimSpace(content) == "" {
+				continue
+			}
+			fmt.Fprintf(b, "---\n# Source: %s\n%s\n", name, content)
+		}
+		return hs, b, "", err
+	}
+
+	// Aggregate all valid manifests into one big doc.
+	fileWritten := make(map[string]bool)
+
+	if includeCrds {
+		for _, crd := range ch.CRDObjects() {
+			if outputDir == "" {
+				fmt.Fprintf(b, "---\n# Source: %s\n%s\n", crd.Name, string(crd.File.Data[:]))
+			} else {
+				err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Name])
+				if err != nil {
+					return hs, b, "", err
+				}
+				fileWritten[crd.Name] = true
+			}
+		}
+	}
+
+	for _, m := range manifests {
+		if outputDir == "" {
+			fmt.Fprintf(b, "---\n# Source: %s\n%s\n", m.Name, m.Content)
+		} else {
+			newDir := outputDir
+			if useReleaseName {
+				newDir = filepath.Join(outputDir, releaseName)
+			}
+			// NOTE: We do not have to worry about the post-renderer because
+			// output dir is only used by `helm template`. In the next major
+			// release, we should move this logic to template only as it is not
+			// used by install or upgrade
+			err = writeToFile(newDir, m.Name, m.Content, fileWritten[m.Name])
+			if err != nil {
+				return hs, b, "", err
+			}
+			fileWritten[m.Name] = true
+		}
+	}
+
+	if pr != nil {
+		b, err = pr.Run(b)
+		if err != nil {
+			return hs, b, notes, errors.Wrap(err, "error while running post render on files")
+		}
+	}
+
+	return hs, b, notes, nil
+}

--- /dev/null
+++ b/pkg/engine/engine.go
@@ -0,0 +1,185 @@
+func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
+	client := action.NewUpgrade(cfg)
+	valueOpts := &values.Options{}
+	var outfmt output.Format
+	var createNamespace bool
+
+	cmd := &cobra.Command{
+		Use:   "upgrade [RELEASE] [CHART]",
+		Short: "upgrade a release",
+		Long:  upgradeDesc,
+		Args:  require.ExactArgs(2),
+		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+			if len(args) == 0 {
+				return compListReleases(toComplete, args, cfg)
+			}
+			if len(args) == 1 {
+				return compListCharts(toComplete, true)
+			}
+			return nil, cobra.ShellCompDirectiveNoFileComp
+		},
+		RunE: func(cmd *cobra.Command, args []string) error {
+			client.Namespace = settings.Namespace()
+
+			// Fixes #7002 - Support reading values from STDIN for `upgrade` command
+			// Must load values AFTER determining if we have to call install so that values loaded from stdin are are not read twice
+			if client.Install {
+				// If a release does not exist, install it.
+				histClient := action.NewHistory(cfg)
+				histClient.Max = 1
+				if _, err := histClient.Run(args[0]); err == driver.ErrReleaseNotFound {
+					// Only print this to stdout for table output
+					if outfmt == output.Table {
+						fmt.Fprintf(out, "Release %q does not exist. Installing it now.\n", args[0])
+					}
+					instClient := action.NewInstall(cfg)
+					instClient.CreateNamespace = createNamespace
+					instClient.ChartPathOptions = client.ChartPathOptions
+					instClient.Force = client.Force
+					instClient.DryRun = client.DryRun
+					instClient.DisableHooks = client.DisableHooks
+					instClient.SkipCRDs = client.SkipCRDs
+					instClient.Timeout = client.Timeout
+					instClient.Wait = client.Wait
+					instClient.WaitForJobs = client.WaitForJobs
+					instClient.Devel = client.Devel
+					instClient.Namespace = client.Namespace
+					instClient.Atomic = client.Atomic
+					instClient.PostRenderer = client.PostRenderer
+					instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation
+					instClient.SubNotes = client.SubNotes
+					instClient.Description = client.Description
+					instClient.DependencyUpdate = client.DependencyUpdate
+					instClient.EnableDNS = client.EnableDNS
+
+					rel, err := runInstall(args, instClient, valueOpts, out)
+					if err != nil {
+						return err
+					}
+					return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false})
+				} else if err != nil {
+					return err
+				}
+			}
+
+			if client.Version == "" && client.Devel {
+				debug("setting version to >0.0.0-0")
+				client.Version = ">0.0.0-0"
+			}
+
+			chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings)
+			if err != nil {
+				return err
+			}
+
+			p := getter.All(settings)
+			vals, err := valueOpts.MergeValues(p)
+			if err != nil {
+				return err
+			}
+
+			// Check chart dependencies to make sure all are present in /charts
+			ch, err := loader.Load(chartPath)
+			if err != nil {
+				return err
+			}
+			if req := ch.Metadata.Dependencies; req != nil {
+				if err := action.CheckDependencies(ch, req); err != nil {
+					err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies")
+					if client.DependencyUpdate {
+						man := &downloader.Manager{
+							Out:              out,
+							ChartPath:        chartPath,
+							Keyring:          client.ChartPathOptions.Keyring,
+							SkipUpdate:       false,
+							Getters:          p,
+							RepositoryConfig: settings.RepositoryConfig,
+							RepositoryCache:  settings.RepositoryCache,
+							Debug:            settings.Debug,
+						}
+						if err := man.Update(); err != nil {
+							return err
+						}
+						// Reload the chart with the updated Chart.lock file.
+						if ch, err = loader.Load(chartPath); err != nil {
+							return errors.Wrap(err, "failed reloading chart after repo update")
+						}
+					} else {
+						return err
+					}
+				}
+			}
+
+			if ch.Metadata.Deprecated {
+				warning("This chart is deprecated")
+			}
+
+			// Create context and prepare the handle of SIGTERM
+			ctx := context.Background()
+			ctx, cancel := context.WithCancel(ctx)
+
+			// Set up channel on which to send signal notifications.
+			// We must use a buffered channel or risk missing the signal
+			// if we're not ready to receive when the signal is sent.
+			cSignal := make(chan os.Signal, 2)
+			signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
+			go func() {
+				<-cSignal
+				fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0])
+				cancel()
+			}()
+
+			rel, err := client.RunWithContext(ctx, args[0], ch, vals)
+			if err != nil {
+				return errors.Wrap(err, "UPGRADE FAILED")
+			}
+
+			if outfmt == output.Table {
+				fmt.Fprintf(out, "Release %q has been upgraded. Happy Helming!\n", args[0])
+			}
+
+			return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false})
+		},
+	}
+
+	f := cmd.Flags()
+	f.BoolVar(&createNamespace, "create-namespace", false, "if --install is set, create the release namespace if not present")
+	f.BoolVarP(&client.Install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
+	f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
+	f.BoolVar(&client.DryRun, "dry-run", false, "simulate an upgrade")
+	f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
+	f.MarkDeprecated("recreate-pods", "functionality will no longer be updated. Consult the documentation for other methods to recreate pods")
+	f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
+	f.BoolVar(&client.DisableHooks, "no-hooks", false, "disable pre/post upgrade hooks")
+	f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the upgrade process will not validate rendered templates against the Kubernetes OpenAPI Schema")
+	f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed when an upgrade is performed with install flag enabled. By default, CRDs are installed if not already present, when an upgrade is performed with install flag enabled")
+	f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
+	f.BoolVar(&client.ResetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
+	f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored")
+	f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")
+	f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
+	f.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically if --atomic is used")
+	f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit")
+	f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails")
+	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
+	f.StringVar(&client.Description, "description", "", "add a custom description")
+	f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
+	f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
+	addChartPathOptionsFlags(f, &client.ChartPathOptions)
+	addValueOptionsFlags(f, valueOpts)
+	bindOutputFlag(cmd, &outfmt)
+	bindPostRenderFlag(cmd, &client.PostRenderer)
+
+	err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+		if len(args) != 2 {
+			return nil, cobra.ShellCompDirectiveNoFileComp
+		}
+		return compVersionFlag(args[1], toComplete)
+	})
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	return cmd
+}

--- /dev/null
+++ b/pkg/engine/engine.go
@@ -0,0 +1,37 @@
+func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) {
+	f.BoolVar(&client.CreateNamespace, "create-namespace", false, "create the release namespace if not present")
+	f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install")
+	f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
+	f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
+	f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
+	f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
+	f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")
+	f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
+	f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)")
+	f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release")
+	f.StringVar(&client.Description, "description", "", "add a custom description")
+	f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
+	f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
+	f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema")
+	f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
+	f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
+	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
+	f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
+	addValueOptionsFlags(f, valueOpts)
+	addChartPathOptionsFlags(f, &client.ChartPathOptions)
+
+	err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+		requiredArgs := 2
+		if client.GenerateName {
+			requiredArgs = 1
+		}
+		if len(args) != requiredArgs {
+			return nil, cobra.ShellCompDirectiveNoFileComp
+		}
+		return compVersionFlag(args[requiredArgs-1], toComplete)
+	})
+
+	if err != nil {
+		log.Fatal(err)
+	}
+}

#!/bin/bash
# From ghcr.io/anonymous2578-data/cve-2023-25165:latest
# bash /workspace/fix-run.sh
set -e

cd /workspace/helm
rm -rf ./pkg/engine/engine_test.go
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
go test  -v -timeout 30s -run ^TestRenderWIthDNS$ helm.sh/helm/v3/pkg/engine

#!/bin/bash
# From ghcr.io/anonymous2578-data/cve-2023-25165:latest
# bash /workspace/unit_test.sh
set -e

cd /workspace/helm
git apply --whitespace=nowarn /workspace/fix.patch
cd pkg/engine && go test -timeout 30s -v


The package total.js before 3.4.8 are vulnerable to Remote Code Execution (RCE) via set.

Improper Control of Generation of Code ('Code Injection')

The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.

Improper Neutralization of Special Elements used in a Command ('Command Injection')

The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.

Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.

exports.set = function(obj, path, value) {
	var cachekey = 'S+' + path;

	if (F.temporary.other[cachekey])
		return F.temporary.other[cachekey](obj, value);

	var arr = parsepath(path);
	var builder = [];

	for (var i = 0; i < arr.length - 1; i++) {
		var type = arr[i + 1] ? (REGISARR.test(arr[i + 1]) ? '[]' : '{}') : '{}';
		var p = 'w' + (arr[i][0] === '[' ? '' : '.') + arr[i];
		builder.push('if(typeof(' + p + ')!==\'object\'||' + p + '==null)' + p + '=' + type + ';');
	}

	var v = arr[arr.length - 1];
	var ispush = v.lastIndexOf('[]') !== -1;
	var a = builder.join(';') + ';var v=typeof(a)===\'function\'?a(U.get(b)):a;w' + (v[0] === '[' ? '' : '.') + (ispush ? v.replace(REGREPLACEARR, '.push(v)') : (v + '=v')) + ';return v';

	if ((/__proto__|constructor|prototype/).test(a))
		throw new Error('Prototype pollution');

	var fn = new Function('w', 'a', 'b', a);
	F.temporary.other[cachekey] = fn;
	fn(obj, value, path);
};

exports.set = function(obj, path, value) {
	var cachekey = 'S+' + path;

	if (F.temporary.other[cachekey])
		return F.temporary.other[cachekey](obj, value);

	var arr = parsepath(path);
	var builder = [];

	for (var i = 0; i < arr.length - 1; i++) {
		var type = arr[i + 1] ? (REGISARR.test(arr[i + 1]) ? '[]' : '{}') : '{}';
		var p = 'w' + (arr[i][0] === '[' ? '' : '.') + arr[i];
		builder.push('if(typeof(' + p + ')!==\'object\'||' + p + '==null)' + p + '=' + type + ';');
	}

	var v = arr[arr.length - 1];
	var ispush = v.lastIndexOf('[]') !== -1;
	var a = builder.join(';') + ';var v=typeof(a)===\'function\'?a(U.get(b)):a;w' + (v[0] === '[' ? '' : '.') + (ispush ? v.replace(REGREPLACEARR, '.push(v)') : (v + '=v')) + ';return v';

	if ((/__proto__|constructor|prototype|eval/).test(a))
		throw new Error('Potential vulnerability');

	var fn = new Function('w', 'a', 'b', a);
	F.temporary.other[cachekey] = fn;
	fn(obj, value, path);
};

--- a/utils.js
+++ b/utils.js
@@ -17,8 +17,8 @@
 	var ispush = v.lastIndexOf('[]') !== -1;
 	var a = builder.join(';') + ';var v=typeof(a)===\'function\'?a(U.get(b)):a;w' + (v[0] === '[' ? '' : '.') + (ispush ? v.replace(REGREPLACEARR, '.push(v)') : (v + '=v')) + ';return v';

-	if ((/__proto__|constructor|prototype/).test(a))
-		throw new Error('Prototype pollution');
+	if ((/__proto__|constructor|prototype|eval/).test(a))
+		throw new Error('Potential vulnerability');

 	var fn = new Function('w', 'a', 'b', a);
 	F.temporary.other[cachekey] = fn;


pretalx 2.3.1 before 2.3.2 allows path traversal in HTML export (a non-default feature). Organizers can trigger the overwriting (with the standard pretalx 404 page content) of an arbitrary file.

External Control of File Name or Path

The product allows user input to control or influence paths or file names that are used in filesystem operations.

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.

def dump_content(destination, path, getter):
    logging.debug(path)
    content = getter(path)
    if path.endswith("/"):
        path = path + "index.html"

    path = Path(destination) / path.lstrip("/")
    path.parent.mkdir(parents=True, exist_ok=True)

    with open(path, "wb") as f:
        f.write(content)
    return content

def get_mediastatic_content(url):
    if url.startswith(settings.STATIC_URL):
        local_path = settings.STATIC_ROOT / url[len(settings.STATIC_URL) :]
    elif url.startswith(settings.MEDIA_URL):
        local_path = settings.MEDIA_ROOT / url[len(settings.MEDIA_URL) :]
    else:
        raise FileNotFoundError()

    with open(local_path, "rb") as f:
        return f.read()

def dump_content(destination, path, getter):
    logging.debug(path)
    content = getter(path)
    if path.endswith("/"):
        path = path + "index.html"

    path = (Path(destination) / path.lstrip("/")).resolve()
    if not Path(destination) in path.parents:
        raise CommandError("Path traversal detected, aborting.")
    path.parent.mkdir(parents=True, exist_ok=True)

    with open(path, "wb") as f:
        f.write(content)
    return content

def get_mediastatic_content(url):
    if url.startswith(settings.STATIC_URL):
        local_path = settings.STATIC_ROOT / url[len(settings.STATIC_URL) :]
    elif url.startswith(settings.MEDIA_URL):
        local_path = settings.MEDIA_ROOT / url[len(settings.MEDIA_URL) :]
    else:
        raise FileNotFoundError()

    # Prevent directory traversal, make sure the path is inside the media or static root
    local_path = local_path.resolve(strict=True)
    if not any(
        path in local_path.parents
        for path in (settings.MEDIA_ROOT, settings.STATIC_ROOT)
    ):
        raise FileNotFoundError()

    with open(local_path, "rb") as f:
        return f.read()

--- a/src/pretalx/agenda/management/commands/export_schedule_html.py
+++ b/src/pretalx/agenda/management/commands/export_schedule_html.py
@@ -4,7 +4,9 @@
     if path.endswith("/"):
         path = path + "index.html"

-    path = Path(destination) / path.lstrip("/")
+    path = (Path(destination) / path.lstrip("/")).resolve()
+    if not Path(destination) in path.parents:
+        raise CommandError("Path traversal detected, aborting.")
     path.parent.mkdir(parents=True, exist_ok=True)

     with open(path, "wb") as f:

--- a/src/pretalx/agenda/management/commands/export_schedule_html.py
+++ b/src/pretalx/agenda/management/commands/export_schedule_html.py
@@ -6,5 +6,13 @@
     else:
         raise FileNotFoundError()

+    # Prevent directory traversal, make sure the path is inside the media or static root
+    local_path = local_path.resolve(strict=True)
+    if not any(
+        path in local_path.parents
+        for path in (settings.MEDIA_ROOT, settings.STATIC_ROOT)
+    ):
+        raise FileNotFoundError()
+
     with open(local_path, "rb") as f:
         return f.read()


GitHub Electron before 1.6.8 allows remote command execution because of a nodeIntegration bypass vulnerability. This also affects all applications that bundle Electron code equivalent to 1.6.8 or earlier. Bypassing the Same Origin Policy (SOP) is a precondition; however, recent Electron versions do not have strict SOP enforcement. Combining an SOP bypass with a privileged URL internally used by Electron, it was possible to execute native Node.js primitives in order to run OS commands on the user's host. Specifically, a chrome-devtools://devtools/bundled/inspector.html window could be used to eval a Node.js child_process.execFile API call.

'use strict'

const events = require('events')
const path = require('path')
const Module = require('module')
const resolvePromise = Promise.resolve.bind(Promise)

// We modified the original process.argv to let node.js load the
// init.js, we need to restore it here.
process.argv.splice(1, 1)

// Clear search paths.
require('../common/reset-search-paths')

// Import common settings.
require('../common/init')

var globalPaths = Module.globalPaths

// Expose public APIs.
globalPaths.push(path.join(__dirname, 'api', 'exports'))

// The global variable will be used by ipc for event dispatching
var v8Util = process.atomBinding('v8_util')

v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter())

// Use electron module after everything is ready.
const electron = require('electron')

// Call webFrame method.
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => {
  electron.webFrame[method](...args)
})

electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  const result = electron.webFrame[method](...args)
  event.sender.send(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, result)
})

electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  const responseCallback = function (result) {
    resolvePromise(result)
      .then((resolvedResult) => {
        event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, null, resolvedResult)
      })
      .catch((resolvedError) => {
        event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedError)
      })
  }
  args.push(responseCallback)
  electron.webFrame[method](...args)
})

// Process command line arguments.
let nodeIntegration = 'false'
let preloadScript = null
let isBackgroundPage = false
let appPath = null
for (let arg of process.argv) {
  if (arg.indexOf('--guest-instance-id=') === 0) {
    // This is a guest web view.
    process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1))
  } else if (arg.indexOf('--opener-id=') === 0) {
    // This is a guest BrowserWindow.
    process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1))
  } else if (arg.indexOf('--node-integration=') === 0) {
    nodeIntegration = arg.substr(arg.indexOf('=') + 1)
  } else if (arg.indexOf('--preload=') === 0) {
    preloadScript = arg.substr(arg.indexOf('=') + 1)
  } else if (arg === '--background-page') {
    isBackgroundPage = true
  } else if (arg.indexOf('--app-path=') === 0) {
    appPath = arg.substr(arg.indexOf('=') + 1)
  }
}

if (window.location.protocol === 'chrome-devtools:') {
  // Override some inspector APIs.
  require('./inspector')
  nodeIntegration = 'true'
} else if (window.location.protocol === 'chrome-extension:') {
  // Add implementations of chrome API.
  require('./chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)
  nodeIntegration = 'false'
} else if (window.location.protocol === 'chrome:') {
  // Disable node integration for chrome UI scheme.
  nodeIntegration = 'false'
} else {
  // Override default web functions.
  require('./override')

  // Inject content scripts.
  require('./content-scripts-injector')

  // Load webview tag implementation.
  if (nodeIntegration === 'true' && process.guestInstanceId == null) {
    require('./web-view/web-view')
    require('./web-view/web-view-attributes')
  }
}

if (nodeIntegration === 'true') {
  // Export node bindings to global.
  global.require = require
  global.module = module

  // Set the __filename to the path of html file if it is file: protocol.
  if (window.location.protocol === 'file:') {
    var pathname = process.platform === 'win32' && window.location.pathname[0] === '/' ? window.location.pathname.substr(1) : window.location.pathname
    global.__filename = path.normalize(decodeURIComponent(pathname))
    global.__dirname = path.dirname(global.__filename)

    // Set module's filename so relative require can work as expected.
    module.filename = global.__filename

    // Also search for module under the html file.
    module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
  } else {
    global.__filename = __filename
    global.__dirname = __dirname

    if (appPath) {
      // Search for module under the app directory
      module.paths = module.paths.concat(Module._nodeModulePaths(appPath))
    }
  }

  // Redirect window.onerror to uncaughtException.
  window.onerror = function (message, filename, lineno, colno, error) {
    if (global.process.listeners('uncaughtException').length > 0) {
      global.process.emit('uncaughtException', error)
      return true
    } else {
      return false
    }
  }
} else {
  // Delete Node's symbols after the Environment has been loaded.
  process.once('loaded', function () {
    delete global.process
    delete global.Buffer
    delete global.setImmediate
    delete global.clearImmediate
    delete global.global
  })
}

// Load the script specfied by the "preload" attribute.
if (preloadScript) {
  try {
    require(preloadScript)
  } catch (error) {
    console.error('Unable to load preload script: ' + preloadScript)
    console.error(error.stack || error.message)
  }
}

'use strict'

const events = require('events')
const path = require('path')
const Module = require('module')
const resolvePromise = Promise.resolve.bind(Promise)

// We modified the original process.argv to let node.js load the
// init.js, we need to restore it here.
process.argv.splice(1, 1)

// Clear search paths.
require('../common/reset-search-paths')

// Import common settings.
require('../common/init')

var globalPaths = Module.globalPaths

// Expose public APIs.
globalPaths.push(path.join(__dirname, 'api', 'exports'))

// The global variable will be used by ipc for event dispatching
var v8Util = process.atomBinding('v8_util')

v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter())

// Use electron module after everything is ready.
const electron = require('electron')

// Call webFrame method.
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => {
  electron.webFrame[method](...args)
})

electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  const result = electron.webFrame[method](...args)
  event.sender.send(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, result)
})

electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  const responseCallback = function (result) {
    resolvePromise(result)
      .then((resolvedResult) => {
        event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, null, resolvedResult)
      })
      .catch((resolvedError) => {
        event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedError)
      })
  }
  args.push(responseCallback)
  electron.webFrame[method](...args)
})

// Process command line arguments.
let nodeIntegration = 'false'
let preloadScript = null
let isBackgroundPage = false
let appPath = null
for (let arg of process.argv) {
  if (arg.indexOf('--guest-instance-id=') === 0) {
    // This is a guest web view.
    process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1))
  } else if (arg.indexOf('--opener-id=') === 0) {
    // This is a guest BrowserWindow.
    process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1))
  } else if (arg.indexOf('--node-integration=') === 0) {
    nodeIntegration = arg.substr(arg.indexOf('=') + 1)
  } else if (arg.indexOf('--preload=') === 0) {
    preloadScript = arg.substr(arg.indexOf('=') + 1)
  } else if (arg === '--background-page') {
    isBackgroundPage = true
  } else if (arg.indexOf('--app-path=') === 0) {
    appPath = arg.substr(arg.indexOf('=') + 1)
  }
}

if (window.location.protocol === 'chrome-devtools:') {
  // Override some inspector APIs.
  require('./inspector')
  nodeIntegration = 'false'
} else if (window.location.protocol === 'chrome-extension:') {
  // Add implementations of chrome API.
  require('./chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)
  nodeIntegration = 'false'
} else if (window.location.protocol === 'chrome:') {
  // Disable node integration for chrome UI scheme.
  nodeIntegration = 'false'
} else {
  // Override default web functions.
  require('./override')

  // Inject content scripts.
  require('./content-scripts-injector')

  // Load webview tag implementation.
  if (nodeIntegration === 'true' && process.guestInstanceId == null) {
    require('./web-view/web-view')
    require('./web-view/web-view-attributes')
  }
}

if (nodeIntegration === 'true') {
  // Export node bindings to global.
  global.require = require
  global.module = module

  // Set the __filename to the path of html file if it is file: protocol.
  if (window.location.protocol === 'file:') {
    var pathname = process.platform === 'win32' && window.location.pathname[0] === '/' ? window.location.pathname.substr(1) : window.location.pathname
    global.__filename = path.normalize(decodeURIComponent(pathname))
    global.__dirname = path.dirname(global.__filename)

    // Set module's filename so relative require can work as expected.
    module.filename = global.__filename

    // Also search for module under the html file.
    module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
  } else {
    global.__filename = __filename
    global.__dirname = __dirname

    if (appPath) {
      // Search for module under the app directory
      module.paths = module.paths.concat(Module._nodeModulePaths(appPath))
    }
  }

  // Redirect window.onerror to uncaughtException.
  window.onerror = function (message, filename, lineno, colno, error) {
    if (global.process.listeners('uncaughtException').length > 0) {
      global.process.emit('uncaughtException', error)
      return true
    } else {
      return false
    }
  }
} else {
  // Delete Node's symbols after the Environment has been loaded.
  process.once('loaded', function () {
    delete global.process
    delete global.Buffer
    delete global.setImmediate
    delete global.clearImmediate
    delete global.global
  })
}

// Load the script specfied by the "preload" attribute.
if (preloadScript) {
  try {
    require(preloadScript)
  } catch (error) {
    console.error('Unable to load preload script: ' + preloadScript)
    console.error(error.stack || error.message)
  }
}

--- a/lib/renderer/init.js
+++ b/lib/renderer/init.js
@@ -78,7 +78,7 @@
 if (window.location.protocol === 'chrome-devtools:') {
   // Override some inspector APIs.
   require('./inspector')
-  nodeIntegration = 'true'
+  nodeIntegration = 'false'
 } else if (window.location.protocol === 'chrome-extension:') {
   // Add implementations of chrome API.
   require('./chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)


Zoraxy is a general purpose HTTP reverse proxy and forwarding tool. A command injection vulnerability in the Web SSH feature allows an authenticated attacker to execute arbitrary commands as root on the host. Zoraxy has a Web SSH terminal feature that allows authenticated users to connect to SSH servers from their browsers. In HandleCreateProxySession the request to create an SSH session is handled. An attacker can exploit the username variable to escape from the bash command and inject arbitrary commands into sshCommand. This is possible, because, unlike hostname and port, the username is not validated or sanitized.

func (i *Instance) CreateNewConnection(listenPort int, username string, remoteIpAddr string, remotePort int) error {
	//Create a gotty instance
	connAddr := remoteIpAddr
	if username != "" {
		connAddr = username + "@" + remoteIpAddr
	}
	configPath := filepath.Join(filepath.Dir(i.ExecPath), ".gotty")
	title := username + "@" + remoteIpAddr
	if remotePort != 22 {
		title = title + ":" + strconv.Itoa(remotePort)
	}

	sshCommand := []string{"ssh", "-t", connAddr, "-p", strconv.Itoa(remotePort)}
	cmd := exec.Command(i.ExecPath, "-w", "-p", strconv.Itoa(listenPort), "--once", "--config", configPath, "--title-format", title, "bash", "-c", strings.Join(sshCommand, " "))
	cmd.Dir = filepath.Dir(i.ExecPath)
	cmd.Env = append(os.Environ(), "TERM=xterm")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	go func() {
		cmd.Run()
		i.Destroy()
	}()
	i.tty = cmd
	i.AssignedPort = listenPort
	i.RemoteAddr = remoteIpAddr
	i.RemotePort = remotePort

	//Create a new proxy agent for this root
	path, err := url.Parse("http://127.0.0.1:" + strconv.Itoa(listenPort))
	if err != nil {
		return err
	}

	//Create new proxy objects to the proxy
	proxy := reverseproxy.NewReverseProxy(path)

	i.conn = proxy
	return nil
}

func (i *Instance) CreateNewConnection(listenPort int, username string, remoteIpAddr string, remotePort int) error {
	//Create a gotty instance
	connAddr := remoteIpAddr
	if username != "" {
		connAddr = username + "@" + remoteIpAddr
	}

	//Trim the space in the username and remote address
	username = strings.TrimSpace(username)
	remoteIpAddr = strings.TrimSpace(remoteIpAddr)

	//Validate the username and remote address
	err := ValidateUsernameAndRemoteAddr(username, remoteIpAddr)
	if err != nil {
		return err
	}

	configPath := filepath.Join(filepath.Dir(i.ExecPath), ".gotty")
	title := username + "@" + remoteIpAddr
	if remotePort != 22 {
		title = title + ":" + strconv.Itoa(remotePort)
	}

	sshCommand := []string{"ssh", "-t", connAddr, "-p", strconv.Itoa(remotePort)}
	cmd := exec.Command(i.ExecPath, "-w", "-p", strconv.Itoa(listenPort), "--once", "--config", configPath, "--title-format", title, "bash", "-c", strings.Join(sshCommand, " "))
	cmd.Dir = filepath.Dir(i.ExecPath)
	cmd.Env = append(os.Environ(), "TERM=xterm")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	go func() {
		cmd.Run()
		i.Destroy()
	}()
	i.tty = cmd
	i.AssignedPort = listenPort
	i.RemoteAddr = remoteIpAddr
	i.RemotePort = remotePort

	//Create a new proxy agent for this root
	path, err := url.Parse("http://127.0.0.1:" + strconv.Itoa(listenPort))
	if err != nil {
		return err
	}

	//Create new proxy objects to the proxy
	proxy := reverseproxy.NewReverseProxy(path)

	i.conn = proxy
	return nil
}

func ValidateUsernameAndRemoteAddr(username string, remoteIpAddr string) error {
	// Validate and sanitize the username to prevent ssh injection
	validUsername := regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
	if !validUsername.MatchString(username) {
		return errors.New("invalid username, only alphanumeric characters, dots, underscores and dashes are allowed")
	}

	//Check if the remoteIpAddr is a valid ipv4 or ipv6 address
	if net.ParseIP(remoteIpAddr) != nil {
		//A valid IP address do not need further validation
		return nil
	}

	// Validate and sanitize the remote domain to prevent injection
	validRemoteAddr := regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
	if !validRemoteAddr.MatchString(remoteIpAddr) {
		return errors.New("invalid remote address, only alphanumeric characters, dots, underscores and dashes are allowed")
	}

	return nil
}

func HandleCreateProxySession(w http.ResponseWriter, r *http.Request) {
	//Get what ip address and port to connect to
	ipaddr, err := utils.PostPara(r, "ipaddr")
	if err != nil {
		http.Error(w, "Invalid Usage", http.StatusInternalServerError)
		return
	}

	portString, err := utils.PostPara(r, "port")
	if err != nil {
		portString = "22"
	}

	username, err := utils.PostPara(r, "username")
	if err != nil {
		username = ""
	}

	port, err := strconv.Atoi(portString)
	if err != nil {
		utils.SendErrorResponse(w, "invalid port number given")
		return
	}

	if !*allowSshLoopback {
		//Not allow loopback connections
		if sshprox.IsLoopbackIPOrDomain(ipaddr) {
			//Request target is loopback
			utils.SendErrorResponse(w, "loopback web ssh connection is not enabled on this host")
			return
		}
	}

	//Check if the target is a valid ssh endpoint
	if !sshprox.IsSSHConnectable(ipaddr, port) {
		utils.SendErrorResponse(w, ipaddr+":"+strconv.Itoa(port)+" is not a valid SSH server")
		return
	}

	//Create a new proxy instance
	instance, err := webSshManager.NewSSHProxy("./tmp/gotty")
	if err != nil {
		utils.SendErrorResponse(w, strings.ReplaceAll(err.Error(), "\\", "/"))
		return
	}

	//Create an ssh process to the target address
	err = instance.CreateNewConnection(webSshManager.GetNextPort(), username, ipaddr, port)
	if err != nil {
		utils.SendErrorResponse(w, err.Error())
		return
	}

	//Return the instance uuid
	js, _ := json.Marshal(instance.UUID)
	utils.SendJSONResponse(w, string(js))
}

--- a/src/mod/sshprox/sshprox.go
+++ b/src/mod/sshprox/sshprox.go
@@ -4,6 +4,17 @@
 	if username != "" {
 		connAddr = username + "@" + remoteIpAddr
 	}
+
+	//Trim the space in the username and remote address
+	username = strings.TrimSpace(username)
+	remoteIpAddr = strings.TrimSpace(remoteIpAddr)
+
+	//Validate the username and remote address
+	err := ValidateUsernameAndRemoteAddr(username, remoteIpAddr)
+	if err != nil {
+		return err
+	}
+
 	configPath := filepath.Join(filepath.Dir(i.ExecPath), ".gotty")
 	title := username + "@" + remoteIpAddr
 	if remotePort != 22 {

--- /dev/null
+++ b/src/mod/sshprox/sshprox.go
@@ -0,0 +1,21 @@
+func ValidateUsernameAndRemoteAddr(username string, remoteIpAddr string) error {
+	// Validate and sanitize the username to prevent ssh injection
+	validUsername := regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
+	if !validUsername.MatchString(username) {
+		return errors.New("invalid username, only alphanumeric characters, dots, underscores and dashes are allowed")
+	}
+
+	//Check if the remoteIpAddr is a valid ipv4 or ipv6 address
+	if net.ParseIP(remoteIpAddr) != nil {
+		//A valid IP address do not need further validation
+		return nil
+	}
+
+	// Validate and sanitize the remote domain to prevent injection
+	validRemoteAddr := regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
+	if !validRemoteAddr.MatchString(remoteIpAddr) {
+		return errors.New("invalid remote address, only alphanumeric characters, dots, underscores and dashes are allowed")
+	}
+
+	return nil
+}

--- /dev/null
+++ b/src/mod/sshprox/sshprox.go
@@ -0,0 +1,57 @@
+func HandleCreateProxySession(w http.ResponseWriter, r *http.Request) {
+	//Get what ip address and port to connect to
+	ipaddr, err := utils.PostPara(r, "ipaddr")
+	if err != nil {
+		http.Error(w, "Invalid Usage", http.StatusInternalServerError)
+		return
+	}
+
+	portString, err := utils.PostPara(r, "port")
+	if err != nil {
+		portString = "22"
+	}
+
+	username, err := utils.PostPara(r, "username")
+	if err != nil {
+		username = ""
+	}
+
+	port, err := strconv.Atoi(portString)
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid port number given")
+		return
+	}
+
+	if !*allowSshLoopback {
+		//Not allow loopback connections
+		if sshprox.IsLoopbackIPOrDomain(ipaddr) {
+			//Request target is loopback
+			utils.SendErrorResponse(w, "loopback web ssh connection is not enabled on this host")
+			return
+		}
+	}
+
+	//Check if the target is a valid ssh endpoint
+	if !sshprox.IsSSHConnectable(ipaddr, port) {
+		utils.SendErrorResponse(w, ipaddr+":"+strconv.Itoa(port)+" is not a valid SSH server")
+		return
+	}
+
+	//Create a new proxy instance
+	instance, err := webSshManager.NewSSHProxy("./tmp/gotty")
+	if err != nil {
+		utils.SendErrorResponse(w, strings.ReplaceAll(err.Error(), "\\", "/"))
+		return
+	}
+
+	//Create an ssh process to the target address
+	err = instance.CreateNewConnection(webSshManager.GetNextPort(), username, ipaddr, port)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	//Return the instance uuid
+	js, _ := json.Marshal(instance.UUID)
+	utils.SendJSONResponse(w, string(js))
+}

#!/bin/bash
# From ghcr.io/anonymous2578-data/cve-2024-52010:latest
# bash /workspace/fix-run.sh
set -e

cd /workspace/zoraxy
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
cd ./src/mod/sshprox && go test  -timeout 30s -run ^TestCreateNewConnection$ imuslab.com/zoraxy/mod/sshprox


Mattermost fails to check if the requesting user is a guest before performing different actions to public playbooks, resulting a guest being able to view, join, edit, export and archive public playbooks.

Missing Authorization

The product does not perform an authorization check when an actor attempts to access a resource or perform an action.

func (ps *PlatformService) SetTelemetryId(id string) {
	ps.telemetryId = id
}

func (ps *PlatformService) SetTelemetryId(id string) {
	ps.telemetryId = id

	ps.PostTelemetryIdHook()
}

// PostTelemetryIdHook triggers necessary events to propagate telemtery ID
func (ps *PlatformService) PostTelemetryIdHook() {
	ps.regenerateClientConfig()
}

--- a/app/platform/service.go
+++ b/app/platform/service.go
@@ -1,3 +1,10 @@
 func (ps *PlatformService) SetTelemetryId(id string) {
 	ps.telemetryId = id
+
+	ps.PostTelemetryIdHook()
 }
+
+// PostTelemetryIdHook triggers necessary events to propagate telemtery ID
+func (ps *PlatformService) PostTelemetryIdHook() {
+	ps.regenerateClientConfig()
+}