Improper authorization in aedes version <0.35.0 will publish a LWT in a channel when a client is not authorized.

Improper Authorization

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

Execution with Unnecessary Privileges

The product performs an operation at a privilege level that is higher than the minimum level required, which creates new weaknesses or amplifies the consequences of other weaknesses.

Improper Privilege Management

The product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor.

  function finish () {
    if (!that.disconnected && that.will) {
      that.broker.publish(that.will, that, function (err) {
        if (!err) {
          that.broker.persistence.delWill({
            id: that.id,
            brokerId: that.broker.id
          }, nop)
        }
      })
      that.will = null // this function might be called twice
    }

    conn.removeAllListeners('error')
    conn.on('error', nop)

    if (that.broker.clients[that.id]) {
      that.broker.unregisterClient(that)
    }

    // hack to clean up the write callbacks
    // supports streams2 & streams3, so node 0.10, 0.11, and iojs
    var state = that.conn._writableState
    var list = (state.getBuffer && state.getBuffer()) || state.buffer
    list.forEach(drainRequest)

    // clear up the drain event listeners
    that.conn.emit('drain')
    that.conn.removeAllListeners('drain')

    if (conn.destroySoon) {
      conn.destroySoon()
    } if (conn.destroy) {
      conn.destroy()
    } else {
      conn.end()
    }

    if (typeof done === 'function') {
      done()
    }
  }
}

  function finish () {
    if (!that.disconnected && that.will) {
      that.broker.authorizePublish(that, that.will, function (err) {
        if (!err) {
          that.broker.publish(that.will, that, done)
        } else {
          done()
        }

        function done (err) {
          if (!err) {
            that.broker.persistence.delWill({
              id: that.id,
              brokerId: that.broker.id
            }, nop)
          }
        }
      })
      that.will = null // this function might be called twice
    }

    conn.removeAllListeners('error')
    conn.on('error', nop)

    if (that.broker.clients[that.id]) {
      that.broker.unregisterClient(that)
    }

    // hack to clean up the write callbacks
    // supports streams2 & streams3, so node 0.10, 0.11, and iojs
    var state = that.conn._writableState
    var list = (state.getBuffer && state.getBuffer()) || state.buffer
    list.forEach(drainRequest)

    // clear up the drain event listeners
    that.conn.emit('drain')
    that.conn.removeAllListeners('drain')

    if (conn.destroySoon) {
      conn.destroySoon()
    } if (conn.destroy) {
      conn.destroy()
    } else {
      conn.end()
    }

    if (typeof done === 'function') {
      done()
    }
  }
}

--- a/lib/client.js
+++ b/lib/client.js
@@ -1,11 +1,19 @@
   function finish () {
     if (!that.disconnected && that.will) {
-      that.broker.publish(that.will, that, function (err) {
+      that.broker.authorizePublish(that, that.will, function (err) {
         if (!err) {
-          that.broker.persistence.delWill({
-            id: that.id,
-            brokerId: that.broker.id
-          }, nop)
+          that.broker.publish(that.will, that, done)
+        } else {
+          done()
+        }
+
+        function done (err) {
+          if (!err) {
+            that.broker.persistence.delWill({
+              id: that.id,
+              brokerId: that.broker.id
+            }, nop)
+          }
         }
       })
       that.will = null // this function might be called twice

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

cd /workspace/aedes
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
npx tape ./test/will.js

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

cd /workspace/aedes
git apply --whitespace=nowarn /workspace/fix.patch
npx tape $(find test -name '*.js' | grep -v 'test/basic.js' | grep -v 'test/meta.js' | grep -v 'test/meta.js')


HashiCorp Consul 1.8.1 up to 1.11.8, 1.12.4, and 1.13.1 do not properly validate the node or segment names prior to interpolation and usage in JWT claim assertions with the auto config RPC. Fixed in 1.11.9, 1.12.5, and 1.13.2."

Missing Authorization

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

func (a *jwtAuthorizer) Authorize(req *pbautoconf.AutoConfigRequest) (AutoConfigOptions, error) {
	// perform basic JWT Authorization
	identity, err := a.validator.ValidateLogin(context.Background(), req.JWT)
	if err != nil {
		// TODO (autoconf) maybe we should add a more generic permission denied error not tied to the ACL package?
		return AutoConfigOptions{}, acl.PermissionDenied("Failed JWT authorization: %v", err)
	}

	varMap := map[string]string{
		"node":      req.Node,
		"segment":   req.Segment,
		"partition": req.PartitionOrDefault(),
	}

	for _, raw := range a.claimAssertions {
		// validate and fill any HIL
		filled, err := template.InterpolateHIL(raw, varMap, true)
		if err != nil {
			return AutoConfigOptions{}, fmt.Errorf("Failed to render claim assertion template %q: %w", raw, err)
		}

		evaluator, err := bexpr.CreateEvaluatorForType(filled, nil, identity.SelectableFields)
		if err != nil {
			return AutoConfigOptions{}, fmt.Errorf("Failed to create evaluator for claim assertion %q: %w", filled, err)
		}

		ok, err := evaluator.Evaluate(identity.SelectableFields)
		if err != nil {
			return AutoConfigOptions{}, fmt.Errorf("Failed to execute claim assertion %q: %w", filled, err)
		}

		if !ok {
			return AutoConfigOptions{}, acl.PermissionDenied("Failed JWT claim assertion")
		}
	}

	opts := AutoConfigOptions{
		NodeName:    req.Node,
		SegmentName: req.Segment,
		Partition:   req.Partition,
	}

	if req.CSR != "" {
		csr, id, err := parseAutoConfigCSR(req.CSR)
		if err != nil {
			return AutoConfigOptions{}, err
		}

		if id.Agent != req.Node || !structs.EqualPartitions(id.Partition, req.Partition) {
			return AutoConfigOptions{},
				fmt.Errorf("Spiffe ID agent name (%s) of the certificate signing request is not for the correct node (%s)",
					printNodeName(id.Agent, id.Partition),
					printNodeName(req.Node, req.Partition),
				)
		}

		opts.CSR = csr
		opts.SpiffeID = id
	}

	return opts, nil
}

var invalidSegmentName = regexp.MustCompile("[`'\"\\s]+")

func (a *jwtAuthorizer) Authorize(req *pbautoconf.AutoConfigRequest) (AutoConfigOptions, error) {
	// perform basic JWT Authorization
	identity, err := a.validator.ValidateLogin(context.Background(), req.JWT)
	if err != nil {
		// TODO (autoconf) maybe we should add a more generic permission denied error not tied to the ACL package?
		return AutoConfigOptions{}, acl.PermissionDenied("Failed JWT authorization: %v", err)
	}

	// Ensure provided data cannot escape the RHS of a bexpr for security.
	// This is not the cleanest way to prevent this behavior. Ideally, the bexpr would allow us to
	// inject a variable on the RHS for comparison as well, but it would be a complex change to implement
	// that would likely break backwards-compatibility in certain circumstances.
	if dns.InvalidNameRe.MatchString(req.Node) {
		return AutoConfigOptions{}, fmt.Errorf("Invalid request field. %v = `%v`", "node", req.Node)
	}
	if invalidSegmentName.MatchString(req.Segment) {
		return AutoConfigOptions{}, fmt.Errorf("Invalid request field. %v = `%v`", "segment", req.Segment)
	}
	if req.Partition != "" && !dns.IsValidLabel(req.Partition) {
		return AutoConfigOptions{}, fmt.Errorf("Invalid request field. %v = `%v`", "partition", req.Partition)
	}

	// Ensure that every value in this mapping is safe to interpolate before using it.
	varMap := map[string]string{
		"node":      req.Node,
		"segment":   req.Segment,
		"partition": req.PartitionOrDefault(),
	}

	for _, raw := range a.claimAssertions {
		// validate and fill any HIL
		filled, err := template.InterpolateHIL(raw, varMap, true)
		if err != nil {
			return AutoConfigOptions{}, fmt.Errorf("Failed to render claim assertion template %q: %w", raw, err)
		}

		evaluator, err := bexpr.CreateEvaluatorForType(filled, nil, identity.SelectableFields)
		if err != nil {
			return AutoConfigOptions{}, fmt.Errorf("Failed to create evaluator for claim assertion %q: %w", filled, err)
		}

		ok, err := evaluator.Evaluate(identity.SelectableFields)
		if err != nil {
			return AutoConfigOptions{}, fmt.Errorf("Failed to execute claim assertion %q: %w", filled, err)
		}

		if !ok {
			return AutoConfigOptions{}, acl.PermissionDenied("Failed JWT claim assertion")
		}
	}

	opts := AutoConfigOptions{
		NodeName:    req.Node,
		SegmentName: req.Segment,
		Partition:   req.Partition,
	}

	if req.CSR != "" {
		csr, id, err := parseAutoConfigCSR(req.CSR)
		if err != nil {
			return AutoConfigOptions{}, err
		}

		if id.Agent != req.Node || !structs.EqualPartitions(id.Partition, req.Partition) {
			return AutoConfigOptions{},
				fmt.Errorf("Spiffe ID agent name (%s) of the certificate signing request is not for the correct node (%s)",
					printNodeName(id.Agent, id.Partition),
					printNodeName(req.Node, req.Partition),
				)
		}

		opts.CSR = csr
		opts.SpiffeID = id
	}

	return opts, nil
}

--- a/agent/consul/auto_config_endpoint.go
+++ b/agent/consul/auto_config_endpoint.go
@@ -1,3 +1,5 @@
+var invalidSegmentName = regexp.MustCompile("[`'\"\\s]+")
+
 func (a *jwtAuthorizer) Authorize(req *pbautoconf.AutoConfigRequest) (AutoConfigOptions, error) {
 	// perform basic JWT Authorization
 	identity, err := a.validator.ValidateLogin(context.Background(), req.JWT)
@@ -6,6 +8,21 @@
 		return AutoConfigOptions{}, acl.PermissionDenied("Failed JWT authorization: %v", err)
 	}

+	// Ensure provided data cannot escape the RHS of a bexpr for security.
+	// This is not the cleanest way to prevent this behavior. Ideally, the bexpr would allow us to
+	// inject a variable on the RHS for comparison as well, but it would be a complex change to implement
+	// that would likely break backwards-compatibility in certain circumstances.
+	if dns.InvalidNameRe.MatchString(req.Node) {
+		return AutoConfigOptions{}, fmt.Errorf("Invalid request field. %v = `%v`", "node", req.Node)
+	}
+	if invalidSegmentName.MatchString(req.Segment) {
+		return AutoConfigOptions{}, fmt.Errorf("Invalid request field. %v = `%v`", "segment", req.Segment)
+	}
+	if req.Partition != "" && !dns.IsValidLabel(req.Partition) {
+		return AutoConfigOptions{}, fmt.Errorf("Invalid request field. %v = `%v`", "partition", req.Partition)
+	}
+
+	// Ensure that every value in this mapping is safe to interpolate before using it.
 	varMap := map[string]string{
 		"node":      req.Node,
 		"segment":   req.Segment,

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

cd /workspace/consul
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
go test  -timeout 30s -run ^TestAutoConfigInitialConfiguration$ github.com/hashicorp/consul/agent/consul

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

cd /workspace/consul
git apply --whitespace=nowarn /workspace/fix.patch
go test -timeout 30s -run '^(TestAutoConfig_updateTLSCertificatesInConfig|TestAutoConfig_updateACLsInConfig|TestAutoConfig_updateGossipEncryptionInConfig|TestAutoConfig_baseConfig|TestAutoConfigInitialConfiguration|TestAutoConfig_updateTLSSettingsInConfig|TestAutoConfig_updateJoinAddressesInConfig)$' github.com/hashicorp/consul/agent/consul


views/switch.py in django-grappelli (aka Django Grappelli) before 2.15.2 attempts to prevent external redirection with startswith("/") but this does not consider a protocol-relative URL (e.g., //example.com) attack.

URL Redirection to Untrusted Site ('Open Redirect')

The web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a redirect.

def switch_user(request, object_id):

    # current/session user
    current_user = request.user
    session_user = request.session.get("original_user", {"id": current_user.id, "username": current_user.get_username()})

    # check redirect
    redirect_url = request.GET.get("redirect", None)
    if redirect_url is None or not redirect_url.startswith("/"):
        raise Http404()

    # check original_user
    try:
        original_user = User.objects.get(pk=session_user["id"], is_staff=True)
        if not SWITCH_USER_ORIGINAL(original_user):
            messages.add_message(request, messages.ERROR, _("Permission denied."))
            return redirect(request.GET.get("redirect"))
    except ObjectDoesNotExist:
        msg = _('%(name)s object with primary key %(key)r does not exist.') % {'name': "User", 'key': escape(session_user["id"])}
        messages.add_message(request, messages.ERROR, msg)
        return redirect(request.GET.get("redirect"))

    # check new user
    try:
        target_user = User.objects.get(pk=object_id, is_staff=True)
        if target_user != original_user and not SWITCH_USER_TARGET(original_user, target_user):
            messages.add_message(request, messages.ERROR, _("Permission denied."))
            return redirect(request.GET.get("redirect"))
    except ObjectDoesNotExist:
        msg = _('%(name)s object with primary key %(key)r does not exist.') % {'name': "User", 'key': escape(object_id)}
        messages.add_message(request, messages.ERROR, msg)
        return redirect(request.GET.get("redirect"))

    # find backend
    if not hasattr(target_user, 'backend'):
        for backend in settings.AUTHENTICATION_BACKENDS:
            if target_user == load_backend(backend).get_user(target_user.pk):
                target_user.backend = backend
                break

    # target user login, set original as session
    if hasattr(target_user, 'backend'):
        login(request, target_user)
        if original_user.id != target_user.id:
            request.session["original_user"] = {"id": original_user.id, "username": original_user.get_username()}

    return redirect(request.GET.get("redirect"))

def switch_user(request, object_id):

    # current/session user
    current_user = request.user
    session_user = request.session.get("original_user", {"id": current_user.id, "username": current_user.get_username()})

    # check redirect
    redirect_url = request.GET.get("redirect", None)
    if redirect_url is None or not \
        url_has_allowed_host_and_scheme(
            url=redirect_url,
            allowed_hosts={request.get_host()},
            require_https=request.is_secure(),
        ):
        raise Http404()

    # check original_user
    try:
        original_user = User.objects.get(pk=session_user["id"], is_staff=True)
        if not SWITCH_USER_ORIGINAL(original_user):
            messages.add_message(request, messages.ERROR, _("Permission denied."))
            return redirect(redirect_url)
    except ObjectDoesNotExist:
        msg = _('%(name)s object with primary key %(key)r does not exist.') % {'name': "User", 'key': escape(session_user["id"])}
        messages.add_message(request, messages.ERROR, msg)
        return redirect(redirect_url)

    # check new user
    try:
        target_user = User.objects.get(pk=object_id, is_staff=True)
        if target_user != original_user and not SWITCH_USER_TARGET(original_user, target_user):
            messages.add_message(request, messages.ERROR, _("Permission denied."))
            return redirect(redirect_url)
    except ObjectDoesNotExist:
        msg = _('%(name)s object with primary key %(key)r does not exist.') % {'name': "User", 'key': escape(object_id)}
        messages.add_message(request, messages.ERROR, msg)
        return redirect(redirect_url)

    # find backend
    if not hasattr(target_user, 'backend'):
        for backend in settings.AUTHENTICATION_BACKENDS:
            if target_user == load_backend(backend).get_user(target_user.pk):
                target_user.backend = backend
                break

    # target user login, set original as session
    if hasattr(target_user, 'backend'):
        login(request, target_user)
        if original_user.id != target_user.id:
            request.session["original_user"] = {"id": original_user.id, "username": original_user.get_username()}

    return redirect(redirect_url)

--- a/grappelli/views/switch.py
+++ b/grappelli/views/switch.py
@@ -6,30 +6,35 @@

     # check redirect
     redirect_url = request.GET.get("redirect", None)
-    if redirect_url is None or not redirect_url.startswith("/"):
+    if redirect_url is None or not \
+        url_has_allowed_host_and_scheme(
+            url=redirect_url,
+            allowed_hosts={request.get_host()},
+            require_https=request.is_secure(),
+        ):
         raise Http404()
-
+
     # check original_user
     try:
         original_user = User.objects.get(pk=session_user["id"], is_staff=True)
         if not SWITCH_USER_ORIGINAL(original_user):
             messages.add_message(request, messages.ERROR, _("Permission denied."))
-            return redirect(request.GET.get("redirect"))
+            return redirect(redirect_url)
     except ObjectDoesNotExist:
         msg = _('%(name)s object with primary key %(key)r does not exist.') % {'name': "User", 'key': escape(session_user["id"])}
         messages.add_message(request, messages.ERROR, msg)
-        return redirect(request.GET.get("redirect"))
+        return redirect(redirect_url)

     # check new user
     try:
         target_user = User.objects.get(pk=object_id, is_staff=True)
         if target_user != original_user and not SWITCH_USER_TARGET(original_user, target_user):
             messages.add_message(request, messages.ERROR, _("Permission denied."))
-            return redirect(request.GET.get("redirect"))
+            return redirect(redirect_url)
     except ObjectDoesNotExist:
         msg = _('%(name)s object with primary key %(key)r does not exist.') % {'name': "User", 'key': escape(object_id)}
         messages.add_message(request, messages.ERROR, msg)
-        return redirect(request.GET.get("redirect"))
+        return redirect(redirect_url)

     # find backend
     if not hasattr(target_user, 'backend'):
@@ -44,4 +49,4 @@
         if original_user.id != target_user.id:
             request.session["original_user"] = {"id": original_user.id, "username": original_user.get_username()}

-    return redirect(request.GET.get("redirect"))
+    return redirect(redirect_url)


The package trailing-slash before 2.0.1 are vulnerable to Open Redirect via the use of trailing double slashes in the URL when accessing the vulnerable endpoint (such as https://example.com//attacker.example/). The vulnerable code is in index.js::createTrailing(), as the web server uses relative URLs instead of absolute URLs.

function createTrailing (_options, _next) {
  var next = typeof _options === 'function' ? _options : _next
  var options = typeof _options === 'object' ? _options : {}
  var status = options.status || 302
  var slash = typeof options.slash === 'undefined' ? true : options.slash
  var middleware = !next

  return function trailingSlash () {
    var args = Array.prototype.slice.call(arguments)
    var done = middleware ? args.slice(-1)[0] : next

    var req = args[0]
    var res = args[1]
    var url = u.parse(req.url)
    var length = url.pathname.length
    var hasSlash = url.pathname.charAt(length - 1) === '/'

    if (hasSlash === slash) {
      if (middleware) {
        return done()
      }
      return next.apply(null, args)
    }

    if (slash) {
      url.pathname = url.pathname + '/'
    } else {
      url.pathname = url.pathname.slice(0, -1)
    }

    res.statusCode = status
    res.setHeader('Location', u.format(url))
    res.end()
  }
}

function createTrailing (_options, _next) {
  var next = typeof _options === 'function' ? _options : _next
  var options = typeof _options === 'object' ? _options : {}
  var status = options.status || 302
  var slash = typeof options.slash === 'undefined' ? true : options.slash
  var middleware = !next

  return function trailingSlash () {
    var args = Array.prototype.slice.call(arguments)
    var done = middleware ? args.slice(-1)[0] : next

    var req = args[0]
    var res = args[1]
    var url = u.parse(req.url)
    var length = url.pathname.length
    var hasSlash = url.pathname.charAt(length - 1) === '/'

    if (hasSlash === slash) {
      if (middleware) {
        return done()
      }
      return next.apply(null, args)
    }

    if (slash) {
      url.pathname = url.pathname + '/'
    } else {
      url.pathname = url.pathname.slice(0, -1)
    }

    res.statusCode = status
    res.setHeader('Location', escapeLeadingSlashes(u.format(url)))
    res.end()
  }
}

function escapeLeadingSlashes (url) {
  if (url.slice(0, 2) === '//') {
    return '/%2F' + url.slice(2)
  }
  return url
}

--- a/index.js
+++ b/index.js
@@ -29,7 +29,7 @@
     }

     res.statusCode = status
-    res.setHeader('Location', u.format(url))
+    res.setHeader('Location', escapeLeadingSlashes(u.format(url)))
     res.end()
   }
 }

--- /dev/null
+++ b/index.js
@@ -0,0 +1,6 @@
+function escapeLeadingSlashes (url) {
+  if (url.slice(0, 2) === '//') {
+    return '/%2F' + url.slice(2)
+  }
+  return url
+}

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

cd /workspace/trailing-slash
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
npx tape test.js

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

cd /workspace/trailing-slash
git apply --whitespace=nowarn /workspace/fix.patch
npx tape test.js


An issue was discovered in llama_index before 0.10.38. download/integration.py includes an exec call for import {cls_name}.

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.

def download_integration(module_str: str, module_import_str: str, cls_name: str) -> Any:
    """Returns an integration class by first pip installing its parent module."""
    try:
        pip_install(module_str)  # this works for any integration not just packs
    except Exception as e:
        raise Exception(f"Failed to pip install `{module_str}`") from e

    try:
        exec(f"from {module_import_str} import {cls_name}")
        module_spec = importlib.util.find_spec(module_import_str)
        module = importlib.util.module_from_spec(module_spec)
        module_spec.loader.exec_module(module)
        pack_cls = getattr(module, cls_name)
    except ImportError as e:
        raise ImportError(f"Unable to import {cls_name}") from e
    return pack_cls

def download_integration(module_str: str, module_import_str: str, cls_name: str) -> Any:
    """Returns an integration class by first pip installing its parent module."""
    try:
        pip_install(module_str)  # this works for any integration not just packs
    except Exception as e:
        raise Exception(f"Failed to pip install `{module_str}`") from e

    try:
        module_spec = importlib.util.find_spec(module_import_str)
        module = importlib.util.module_from_spec(module_spec)
        module_spec.loader.exec_module(module)
        pack_cls = getattr(module, cls_name)
    except ImportError as e:
        raise ImportError(f"Unable to import {cls_name}") from e
    return pack_cls

--- a/llama-index-core/llama_index/core/download/integration.py
+++ b/llama-index-core/llama_index/core/download/integration.py
@@ -6,7 +6,6 @@
         raise Exception(f"Failed to pip install `{module_str}`") from e

     try:
-        exec(f"from {module_import_str} import {cls_name}")
         module_spec = importlib.util.find_spec(module_import_str)
         module = importlib.util.module_from_spec(module_spec)
         module_spec.loader.exec_module(module)


A deserialization issue discovered in inikulin replicator before 1.0.4 allows remote attackers to run arbitrary code via the fromSerializable function in TypedArray object.

Deserialization of Untrusted Data

The product deserializes untrusted data without sufficiently ensuring that the resulting data will be valid.

var ARRAY_BUFFER_SUPPORTED = typeof ArrayBuffer === 'function';
var MAP_SUPPORTED          = typeof Map === 'function';
var SET_SUPPORTED          = typeof Set === 'function';

var TYPED_ARRAY_CTORS = [
    'Int8Array',
    'Uint8Array',
    'Uint8ClampedArray',
    'Int16Array',
    'Uint16Array',
    'Int32Array',
    'Uint32Array',
    'Float32Array',
    'Float64Array'
];

        fromSerializable: function (val) {
            return typeof GLOBAL[val.ctorName] === 'function' ? new GLOBAL[val.ctorName](val.arr) : val.arr;
        }

var TYPED_ARRAY_CTORS = {
    'Int8Array':         Int8Array,
    'Uint8Array':        Uint8Array,
    'Uint8ClampedArray': Uint8ClampedArray,
    'Int16Array':        Int16Array,
    'Uint16Array':       Uint16Array,
    'Int32Array':        Int32Array,
    'Uint32Array':       Uint32Array,
    'Float32Array':      Float32Array,
    'Float64Array':      Float64Array
};

function isFunction (value) {
    return typeof value === 'function';
}

var ARRAY_BUFFER_SUPPORTED = isFunction(ArrayBuffer);
var MAP_SUPPORTED          = isFunction(Map);
var SET_SUPPORTED          = isFunction(Set);

var TYPED_ARRAY_SUPPORTED  = function (typeName) {
    return isFunction(TYPED_ARRAY_CTORS[typeName]);
};

        fromSerializable: function (val) {
            return TYPED_ARRAY_SUPPORTED(val.ctorName) ? new TYPED_ARRAY_CTORS[val.ctorName](val.arr) : val.arr;
        }

--- a/index.js
+++ b/index.js
@@ -1,15 +1,23 @@
-var ARRAY_BUFFER_SUPPORTED = typeof ArrayBuffer === 'function';
-var MAP_SUPPORTED          = typeof Map === 'function';
-var SET_SUPPORTED          = typeof Set === 'function';
+var TYPED_ARRAY_CTORS = {
+    'Int8Array':         Int8Array,
+    'Uint8Array':        Uint8Array,
+    'Uint8ClampedArray': Uint8ClampedArray,
+    'Int16Array':        Int16Array,
+    'Uint16Array':       Uint16Array,
+    'Int32Array':        Int32Array,
+    'Uint32Array':       Uint32Array,
+    'Float32Array':      Float32Array,
+    'Float64Array':      Float64Array
+};

-var TYPED_ARRAY_CTORS = [
-    'Int8Array',
-    'Uint8Array',
-    'Uint8ClampedArray',
-    'Int16Array',
-    'Uint16Array',
-    'Int32Array',
-    'Uint32Array',
-    'Float32Array',
-    'Float64Array'
-];
+function isFunction (value) {
+    return typeof value === 'function';
+}
+
+var ARRAY_BUFFER_SUPPORTED = isFunction(ArrayBuffer);
+var MAP_SUPPORTED          = isFunction(Map);
+var SET_SUPPORTED          = isFunction(Set);
+
+var TYPED_ARRAY_SUPPORTED  = function (typeName) {
+    return isFunction(TYPED_ARRAY_CTORS[typeName]);
+};

--- a/index.js
+++ b/index.js
@@ -1,3 +1,3 @@
         fromSerializable: function (val) {
-            return typeof GLOBAL[val.ctorName] === 'function' ? new GLOBAL[val.ctorName](val.arr) : val.arr;
+            return TYPED_ARRAY_SUPPORTED(val.ctorName) ? new TYPED_ARRAY_CTORS[val.ctorName](val.arr) : val.arr;
         }

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

cd /workspace/replicator
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
npx mocha --grep "Should not allow RCE when deserializing TypedArrays" test/test.js

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

cd /workspace/replicator
git apply --whitespace=nowarn /workspace/test.patch
npx mocha --timeout 3000 --grep "Should not allow RCE when deserializing TypedArrays" --invert test/test.js


An improper limitation of path name flaw was found in containernetworking/cni in versions before 0.8.1. When specifying the plugin to load in the 'type' field in the network configuration, it is possible to use special elements such as "../" separators to reference binaries elsewhere on the system. This flaw allows an attacker to execute other existing binaries other than the cni plugins/types, such as 'reboot'. The highest threat from this vulnerability is to confidentiality, integrity, as well as system availability.

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.

func FindInPath(plugin string, paths []string) (string, error) {
	if plugin == "" {
		return "", fmt.Errorf("no plugin name provided")
	}

	if len(paths) == 0 {
		return "", fmt.Errorf("no paths provided")
	}

	for _, path := range paths {
		for _, fe := range ExecutableFileExtensions {
			fullpath := filepath.Join(path, plugin) + fe
			if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
				return fullpath, nil
			}
		}
	}

	return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths)
}

func FindInPath(plugin string, paths []string) (string, error) {
	if plugin == "" {
		return "", fmt.Errorf("no plugin name provided")
	}

	if strings.ContainsRune(plugin, os.PathSeparator) {
		return "", fmt.Errorf("invalid plugin name: %s", plugin)
	}

	if len(paths) == 0 {
		return "", fmt.Errorf("no paths provided")
	}

	for _, path := range paths {
		for _, fe := range ExecutableFileExtensions {
			fullpath := filepath.Join(path, plugin) + fe
			if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
				return fullpath, nil
			}
		}
	}

	return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths)
}

--- a/pkg/invoke/find.go
+++ b/pkg/invoke/find.go
@@ -1,6 +1,10 @@
 func FindInPath(plugin string, paths []string) (string, error) {
 	if plugin == "" {
 		return "", fmt.Errorf("no plugin name provided")
+	}
+
+	if strings.ContainsRune(plugin, os.PathSeparator) {
+		return "", fmt.Errorf("invalid plugin name: %s", plugin)
 	}

 	if len(paths) == 0 {


A flaw was found in the Keycloak Node.js Adapter. This flaw allows an attacker to benefit from an Open Redirect vulnerability in the checkSso function.

module.exports = function (keycloak) {
  return function checkSso (request, response, next) {
    if (request.kauth && request.kauth.grant) {
      return next()
    }

    //  Check SSO process is completed and user is not logged in
    if (request.session.auth_is_check_sso_complete) {
      request.session.auth_is_check_sso_complete = false
      return next()
    }

    //  Keycloak server has just answered that user is not logged in
    if (request.query.error === 'login_required') {
      const urlParts = {
        pathname: request.path,
        query: request.query
      }

      delete urlParts.query.error
      delete urlParts.query.auth_callback
      delete urlParts.query.state

      const cleanUrl = URL.format(urlParts)

      //  Check SSO process is completed
      request.session.auth_is_check_sso_complete = true

      //  Redirect back to the original URL
      return response.redirect(cleanUrl)
    }

    if (keycloak.redirectToLogin(request)) {
      forceCheckSSO(keycloak, request, response)
    } else {
      return keycloak.accessDenied(request, response, next)
    }
  }
}

module.exports = function (keycloak) {
  return function checkSso (request, response, next) {
    if (request.kauth && request.kauth.grant) {
      return next()
    }

    //  Check SSO process is completed and user is not logged in
    if (request.session.auth_is_check_sso_complete) {
      request.session.auth_is_check_sso_complete = false
      return next()
    }

    //  Keycloak server has just answered that user is not logged in
    if (request.query.error === 'login_required') {
      const urlParts = {
        pathname: request.path,
        query: request.query
      }

      delete urlParts.query.error
      delete urlParts.query.auth_callback
      delete urlParts.query.state

      // Collapse leading slashes to a single slash to prevent open redirects
      const cleanUrl = URL.format(urlParts).replace(/^\/+/, '/')

      //  Check SSO process is completed
      request.session.auth_is_check_sso_complete = true

      //  Redirect back to the original URL
      return response.redirect(cleanUrl)
    }

    if (keycloak.redirectToLogin(request)) {
      forceCheckSSO(keycloak, request, response)
    } else {
      return keycloak.accessDenied(request, response, next)
    }
  }
}

--- a/middleware/check-sso.js
+++ b/middleware/check-sso.js
@@ -21,7 +21,8 @@
       delete urlParts.query.auth_callback
       delete urlParts.query.state

-      const cleanUrl = URL.format(urlParts)
+      // Collapse leading slashes to a single slash to prevent open redirects
+      const cleanUrl = URL.format(urlParts).replace(/^\/+/, '/')

       //  Check SSO process is completed
       request.session.auth_is_check_sso_complete = true


Zalando Skipper v0.13.236 is vulnerable to Server-Side Request Forgery (SSRF).

Server-Side Request Forgery (SSRF)

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.

const (
	proxyBufferSize         = 8192
	unknownRouteID          = "_unknownroute_"
	unknownRouteBackendType = "<unknown>"
	unknownRouteBackend     = "<unknown>"
	backendIsProxyHeader    = "X-Skipper-Proxy"

	// Number of loops allowed by default.
	DefaultMaxLoopbacks = 9

	// The default value set for http.Transport.MaxIdleConnsPerHost.
	DefaultIdleConnsPerHost = 64

	// The default period at which the idle connections are forcibly
	// closed.
	DefaultCloseIdleConnsPeriod = 20 * time.Second

	// DefaultResponseHeaderTimeout, the default response header timeout
	DefaultResponseHeaderTimeout = 60 * time.Second

	// DefaultExpectContinueTimeout, the default timeout to expect
	// a response for a 100 Continue request
	DefaultExpectContinueTimeout = 30 * time.Second
)

func mapRequest(ctx *context, requestContext stdlibcontext.Context, removeHopHeaders bool) (*http.Request, *routing.LBEndpoint, error) {
	var endpoint *routing.LBEndpoint
	r := ctx.request
	rt := ctx.route
	host := ctx.outgoingHost
	stateBag := ctx.StateBag()
	u := r.URL

	switch rt.BackendType {
	case eskip.DynamicBackend:
		setRequestURLFromRequest(u, r)
		setRequestURLForDynamicBackend(u, stateBag)
	case eskip.LBBackend:
		endpoint = setRequestURLForLoadBalancedBackend(u, rt, &routing.LBContext{Request: r, Route: rt, Params: stateBag})
	default:
		u.Scheme = rt.Scheme
		u.Host = rt.Host
	}

	body := r.Body
	if r.ContentLength == 0 {
		body = nil
	}

	rr, err := http.NewRequestWithContext(requestContext, r.Method, u.String(), body)
	if err != nil {
		return nil, endpoint, err
	}

	rr.ContentLength = r.ContentLength
	if removeHopHeaders {
		rr.Header = cloneHeaderExcluding(r.Header, hopHeaders)
	} else {
		rr.Header = cloneHeader(r.Header)
	}
	// Disable default net/http user agent when user agent is not specified
	if _, ok := rr.Header["User-Agent"]; !ok {
		rr.Header["User-Agent"] = []string{""}
	}
	rr.Host = host

	// If there is basic auth configured in the URL we add them as headers
	if u.User != nil {
		up := u.User.String()
		upBase64 := base64.StdEncoding.EncodeToString([]byte(up))
		rr.Header.Add("Authorization", fmt.Sprintf("Basic %s", upBase64))
	}

	if _, ok := stateBag[filters.BackendIsProxyKey]; ok {
		forwardToProxy(r, rr)
	}

	ctxspan := ot.SpanFromContext(r.Context())
	if ctxspan != nil {
		rr = rr.WithContext(ot.ContextWithSpan(rr.Context(), ctxspan))
	}

	return rr, endpoint, nil
}

func forwardToProxy(incoming, outgoing *http.Request) {
	proxyURL := &url.URL{
		Scheme: outgoing.URL.Scheme,
		Host:   outgoing.URL.Host,
	}

	outgoing.URL.Host = incoming.Host
	outgoing.URL.Scheme = schemeFromRequest(incoming)

	outgoing.Header.Set(backendIsProxyHeader, proxyURL.String())
}

func proxyFromHeader(req *http.Request) (*url.URL, error) {
	if u := req.Header.Get(backendIsProxyHeader); u != "" {
		req.Header.Del(backendIsProxyHeader)
		return url.Parse(u)
	}
	return nil, nil
}

func WithParams(p Params) *Proxy {
	if p.IdleConnectionsPerHost <= 0 {
		p.IdleConnectionsPerHost = DefaultIdleConnsPerHost
	}

	if p.CloseIdleConnsPeriod == 0 {
		p.CloseIdleConnsPeriod = DefaultCloseIdleConnsPeriod
	}

	if p.ResponseHeaderTimeout == 0 {
		p.ResponseHeaderTimeout = DefaultResponseHeaderTimeout
	}

	if p.ExpectContinueTimeout == 0 {
		p.ExpectContinueTimeout = DefaultExpectContinueTimeout
	}

	if p.CustomHttpRoundTripperWrap == nil {
		// default wrapper which does nothing
		p.CustomHttpRoundTripperWrap = func(original http.RoundTripper) http.RoundTripper {
			return original
		}
	}

	tr := &http.Transport{
		DialContext: newSkipperDialer(net.Dialer{
			Timeout:   p.Timeout,
			KeepAlive: p.KeepAlive,
			DualStack: p.DualStack,
		}).DialContext,
		TLSHandshakeTimeout:   p.TLSHandshakeTimeout,
		ResponseHeaderTimeout: p.ResponseHeaderTimeout,
		ExpectContinueTimeout: p.ExpectContinueTimeout,
		MaxIdleConns:          p.MaxIdleConns,
		MaxIdleConnsPerHost:   p.IdleConnectionsPerHost,
		IdleConnTimeout:       p.CloseIdleConnsPeriod,
		DisableKeepAlives:     p.DisableHTTPKeepalives,
		Proxy:                 proxyFromHeader,
	}

	quit := make(chan struct{})
	// We need this to reliably fade on DNS change, which is right
	// now not fixed with IdleConnTimeout in the http.Transport.
	// https://github.com/golang/go/issues/23427
	if p.CloseIdleConnsPeriod > 0 {
		go func() {
			for {
				select {
				case <-time.After(p.CloseIdleConnsPeriod):
					tr.CloseIdleConnections()
				case <-quit:
					return
				}
			}
		}()
	}

	if p.ClientTLS != nil {
		tr.TLSClientConfig = p.ClientTLS
	}

	if p.Flags.Insecure() {
		if tr.TLSClientConfig == nil {
			/* #nosec */
			tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
		} else {
			/* #nosec */
			tr.TLSClientConfig.InsecureSkipVerify = true
		}
	}

	m := metrics.Default
	if p.Flags.Debug() {
		m = metrics.Void
	}

	if p.MaxLoopbacks == 0 {
		p.MaxLoopbacks = DefaultMaxLoopbacks
	} else if p.MaxLoopbacks < 0 {
		p.MaxLoopbacks = 0
	}

	defaultHTTPStatus := http.StatusNotFound

	if p.DefaultHTTPStatus >= http.StatusContinue && p.DefaultHTTPStatus <= http.StatusNetworkAuthenticationRequired {
		defaultHTTPStatus = p.DefaultHTTPStatus
	}

	hostname := os.Getenv("HOSTNAME")

	return &Proxy{
		routing:                  p.Routing,
		roundTripper:             p.CustomHttpRoundTripperWrap(tr),
		priorityRoutes:           p.PriorityRoutes,
		flags:                    p.Flags,
		metrics:                  m,
		quit:                     quit,
		flushInterval:            p.FlushInterval,
		experimentalUpgrade:      p.ExperimentalUpgrade,
		experimentalUpgradeAudit: p.ExperimentalUpgradeAudit,
		maxLoops:                 p.MaxLoopbacks,
		breakers:                 p.CircuitBreakers,
		lb:                       p.LoadBalancer,
		limiters:                 p.RateLimiters,
		log:                      &logging.DefaultLog{},
		defaultHTTPStatus:        defaultHTTPStatus,
		tracing:                  newProxyTracing(p.OpenTracing),
		accessLogDisabled:        p.AccessLogDisabled,
		upgradeAuditLogOut:       os.Stdout,
		upgradeAuditLogErr:       os.Stderr,
		clientTLS:                tr.TLSClientConfig,
		hostname:                 hostname,
	}
}

const (
	proxyBufferSize         = 8192
	unknownRouteID          = "_unknownroute_"
	unknownRouteBackendType = "<unknown>"
	unknownRouteBackend     = "<unknown>"

	// Number of loops allowed by default.
	DefaultMaxLoopbacks = 9

	// The default value set for http.Transport.MaxIdleConnsPerHost.
	DefaultIdleConnsPerHost = 64

	// The default period at which the idle connections are forcibly
	// closed.
	DefaultCloseIdleConnsPeriod = 20 * time.Second

	// DefaultResponseHeaderTimeout, the default response header timeout
	DefaultResponseHeaderTimeout = 60 * time.Second

	// DefaultExpectContinueTimeout, the default timeout to expect
	// a response for a 100 Continue request
	DefaultExpectContinueTimeout = 30 * time.Second
)

func mapRequest(ctx *context, requestContext stdlibcontext.Context, removeHopHeaders bool) (*http.Request, *routing.LBEndpoint, error) {
	var endpoint *routing.LBEndpoint
	r := ctx.request
	rt := ctx.route
	host := ctx.outgoingHost
	stateBag := ctx.StateBag()
	u := r.URL

	switch rt.BackendType {
	case eskip.DynamicBackend:
		setRequestURLFromRequest(u, r)
		setRequestURLForDynamicBackend(u, stateBag)
	case eskip.LBBackend:
		endpoint = setRequestURLForLoadBalancedBackend(u, rt, &routing.LBContext{Request: r, Route: rt, Params: stateBag})
	default:
		u.Scheme = rt.Scheme
		u.Host = rt.Host
	}

	body := r.Body
	if r.ContentLength == 0 {
		body = nil
	}

	rr, err := http.NewRequestWithContext(requestContext, r.Method, u.String(), body)
	if err != nil {
		return nil, endpoint, err
	}

	rr.ContentLength = r.ContentLength
	if removeHopHeaders {
		rr.Header = cloneHeaderExcluding(r.Header, hopHeaders)
	} else {
		rr.Header = cloneHeader(r.Header)
	}
	// Disable default net/http user agent when user agent is not specified
	if _, ok := rr.Header["User-Agent"]; !ok {
		rr.Header["User-Agent"] = []string{""}
	}
	rr.Host = host

	// If there is basic auth configured in the URL we add them as headers
	if u.User != nil {
		up := u.User.String()
		upBase64 := base64.StdEncoding.EncodeToString([]byte(up))
		rr.Header.Add("Authorization", fmt.Sprintf("Basic %s", upBase64))
	}

	ctxspan := ot.SpanFromContext(r.Context())
	if ctxspan != nil {
		rr = rr.WithContext(ot.ContextWithSpan(rr.Context(), ctxspan))
	}

	if _, ok := stateBag[filters.BackendIsProxyKey]; ok {
		rr = forwardToProxy(r, rr)
	}

	return rr, endpoint, nil
}

type proxyUrlContextKey struct{}

func forwardToProxy(incoming, outgoing *http.Request) *http.Request {
	proxyURL := &url.URL{
		Scheme: outgoing.URL.Scheme,
		Host:   outgoing.URL.Host,
	}

	outgoing.URL.Host = incoming.Host
	outgoing.URL.Scheme = schemeFromRequest(incoming)

	return outgoing.WithContext(stdlibcontext.WithValue(outgoing.Context(), proxyUrlContextKey{}, proxyURL))
}

func proxyFromContext(req *http.Request) (*url.URL, error) {
	proxyURL, _ := req.Context().Value(proxyUrlContextKey{}).(*url.URL)
	if proxyURL != nil {
		return proxyURL, nil
	}
	return nil, nil
}

func WithParams(p Params) *Proxy {
	if p.IdleConnectionsPerHost <= 0 {
		p.IdleConnectionsPerHost = DefaultIdleConnsPerHost
	}

	if p.CloseIdleConnsPeriod == 0 {
		p.CloseIdleConnsPeriod = DefaultCloseIdleConnsPeriod
	}

	if p.ResponseHeaderTimeout == 0 {
		p.ResponseHeaderTimeout = DefaultResponseHeaderTimeout
	}

	if p.ExpectContinueTimeout == 0 {
		p.ExpectContinueTimeout = DefaultExpectContinueTimeout
	}

	if p.CustomHttpRoundTripperWrap == nil {
		// default wrapper which does nothing
		p.CustomHttpRoundTripperWrap = func(original http.RoundTripper) http.RoundTripper {
			return original
		}
	}

	tr := &http.Transport{
		DialContext: newSkipperDialer(net.Dialer{
			Timeout:   p.Timeout,
			KeepAlive: p.KeepAlive,
			DualStack: p.DualStack,
		}).DialContext,
		TLSHandshakeTimeout:   p.TLSHandshakeTimeout,
		ResponseHeaderTimeout: p.ResponseHeaderTimeout,
		ExpectContinueTimeout: p.ExpectContinueTimeout,
		MaxIdleConns:          p.MaxIdleConns,
		MaxIdleConnsPerHost:   p.IdleConnectionsPerHost,
		IdleConnTimeout:       p.CloseIdleConnsPeriod,
		DisableKeepAlives:     p.DisableHTTPKeepalives,
		Proxy:                 proxyFromContext,
	}

	quit := make(chan struct{})
	// We need this to reliably fade on DNS change, which is right
	// now not fixed with IdleConnTimeout in the http.Transport.
	// https://github.com/golang/go/issues/23427
	if p.CloseIdleConnsPeriod > 0 {
		go func() {
			for {
				select {
				case <-time.After(p.CloseIdleConnsPeriod):
					tr.CloseIdleConnections()
				case <-quit:
					return
				}
			}
		}()
	}

	if p.ClientTLS != nil {
		tr.TLSClientConfig = p.ClientTLS
	}

	if p.Flags.Insecure() {
		if tr.TLSClientConfig == nil {
			/* #nosec */
			tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
		} else {
			/* #nosec */
			tr.TLSClientConfig.InsecureSkipVerify = true
		}
	}

	m := metrics.Default
	if p.Flags.Debug() {
		m = metrics.Void
	}

	if p.MaxLoopbacks == 0 {
		p.MaxLoopbacks = DefaultMaxLoopbacks
	} else if p.MaxLoopbacks < 0 {
		p.MaxLoopbacks = 0
	}

	defaultHTTPStatus := http.StatusNotFound

	if p.DefaultHTTPStatus >= http.StatusContinue && p.DefaultHTTPStatus <= http.StatusNetworkAuthenticationRequired {
		defaultHTTPStatus = p.DefaultHTTPStatus
	}

	hostname := os.Getenv("HOSTNAME")

	return &Proxy{
		routing:                  p.Routing,
		roundTripper:             p.CustomHttpRoundTripperWrap(tr),
		priorityRoutes:           p.PriorityRoutes,
		flags:                    p.Flags,
		metrics:                  m,
		quit:                     quit,
		flushInterval:            p.FlushInterval,
		experimentalUpgrade:      p.ExperimentalUpgrade,
		experimentalUpgradeAudit: p.ExperimentalUpgradeAudit,
		maxLoops:                 p.MaxLoopbacks,
		breakers:                 p.CircuitBreakers,
		lb:                       p.LoadBalancer,
		limiters:                 p.RateLimiters,
		log:                      &logging.DefaultLog{},
		defaultHTTPStatus:        defaultHTTPStatus,
		tracing:                  newProxyTracing(p.OpenTracing),
		accessLogDisabled:        p.AccessLogDisabled,
		upgradeAuditLogOut:       os.Stdout,
		upgradeAuditLogErr:       os.Stderr,
		clientTLS:                tr.TLSClientConfig,
		hostname:                 hostname,
	}
}

--- a/proxy/proxy.go
+++ b/proxy/proxy.go
@@ -3,7 +3,6 @@
 	unknownRouteID          = "_unknownroute_"
 	unknownRouteBackendType = "<unknown>"
 	unknownRouteBackend     = "<unknown>"
-	backendIsProxyHeader    = "X-Skipper-Proxy"

 	// Number of loops allowed by default.
 	DefaultMaxLoopbacks = 9

--- a/proxy/proxy.go
+++ b/proxy/proxy.go
@@ -46,14 +46,14 @@
 		rr.Header.Add("Authorization", fmt.Sprintf("Basic %s", upBase64))
 	}

-	if _, ok := stateBag[filters.BackendIsProxyKey]; ok {
-		forwardToProxy(r, rr)
-	}
-
 	ctxspan := ot.SpanFromContext(r.Context())
 	if ctxspan != nil {
 		rr = rr.WithContext(ot.ContextWithSpan(rr.Context(), ctxspan))
 	}

+	if _, ok := stateBag[filters.BackendIsProxyKey]; ok {
+		rr = forwardToProxy(r, rr)
+	}
+
 	return rr, endpoint, nil
 }

--- a/proxy/proxy.go
+++ b/proxy/proxy.go
@@ -1,4 +1,6 @@
-func forwardToProxy(incoming, outgoing *http.Request) {
+type proxyUrlContextKey struct{}
+
+func forwardToProxy(incoming, outgoing *http.Request) *http.Request {
 	proxyURL := &url.URL{
 		Scheme: outgoing.URL.Scheme,
 		Host:   outgoing.URL.Host,
@@ -7,5 +9,5 @@
 	outgoing.URL.Host = incoming.Host
 	outgoing.URL.Scheme = schemeFromRequest(incoming)

-	outgoing.Header.Set(backendIsProxyHeader, proxyURL.String())
+	return outgoing.WithContext(stdlibcontext.WithValue(outgoing.Context(), proxyUrlContextKey{}, proxyURL))
 }

--- a/proxy/proxy.go
+++ b/proxy/proxy.go
@@ -1,7 +1,7 @@
-func proxyFromHeader(req *http.Request) (*url.URL, error) {
-	if u := req.Header.Get(backendIsProxyHeader); u != "" {
-		req.Header.Del(backendIsProxyHeader)
-		return url.Parse(u)
+func proxyFromContext(req *http.Request) (*url.URL, error) {
+	proxyURL, _ := req.Context().Value(proxyUrlContextKey{}).(*url.URL)
+	if proxyURL != nil {
+		return proxyURL, nil
 	}
 	return nil, nil
 }

--- a/proxy/proxy.go
+++ b/proxy/proxy.go
@@ -35,7 +35,7 @@
 		MaxIdleConnsPerHost:   p.IdleConnectionsPerHost,
 		IdleConnTimeout:       p.CloseIdleConnsPeriod,
 		DisableKeepAlives:     p.DisableHTTPKeepalives,
-		Proxy:                 proxyFromHeader,
+		Proxy:                 proxyFromContext,
 	}

 	quit := make(chan struct{})


A vulnerability has been found in youngerheart nodeserver and classified as critical. Affected by this vulnerability is an unknown functionality of the file nodeserver.js. The manipulation leads to path traversal. The identifier of the patch is c4c0f0138ab5afbac58e03915d446680421bde28. It is recommended to apply a patch to fix this issue. The identifier VDB-218461 was assigned to this vulnerability.

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.

--- a/nodeserver.js
+++ b/nodeserver.js
@@ -1 +1 @@
-    var nowTemp = host.frondend + (request.url.replace('/', '') || host.baseTemp);
+    var nowTemp = (host.frondend + (request.url.replace('/', '') || host.baseTemp)).replace(/\.\./g, '');


Denial-of-service in NodeBB <= v2.8.10 allows unauthenticated attackers to trigger a crash, when invoking `eventName.startsWith()` or `eventName.toString()`, while processing Socket.IO messages via crafted Socket.IO messages containing array or object type for the event name respectively.

Improper Check for Unusual or Exceptional Conditions

The product does not check or incorrectly checks for unusual or exceptional conditions that are not expected to occur frequently during day to day operation of the product.

async function onMessage(socket, payload) {
	if (!payload.data.length) {
		return winston.warn('[socket.io] Empty payload');
	}

	const eventName = payload.data[0];
	const params = typeof payload.data[1] === 'function' ? {} : payload.data[1];
	const callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function () {};

	if (!eventName) {
		return winston.warn('[socket.io] Empty method name');
	}

	const parts = eventName.toString().split('.');
	const namespace = parts[0];
	const methodToCall = parts.reduce((prev, cur) => {
		if (prev !== null && prev[cur] && (!prev.hasOwnProperty || prev.hasOwnProperty(cur))) {
			return prev[cur];
		}
		return null;
	}, Namespaces);

	if (!methodToCall || typeof methodToCall !== 'function') {
		if (process.env.NODE_ENV === 'development') {
			winston.warn(`[socket.io] Unrecognized message: ${eventName}`);
		}
		const escapedName = validator.escape(String(eventName));
		return callback({ message: `[[error:invalid-event, ${escapedName}]]` });
	}

	socket.previousEvents = socket.previousEvents || [];
	socket.previousEvents.push(eventName);
	if (socket.previousEvents.length > 20) {
		socket.previousEvents.shift();
	}

	if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) {
		winston.warn(`[socket.io] Too many emits! Disconnecting uid : ${socket.uid}. Events : ${socket.previousEvents}`);
		return socket.disconnect();
	}

	try {
		await checkMaintenance(socket);
		await validateSession(socket, '[[error:revalidate-failure]]');

		if (Namespaces[namespace].before) {
			await Namespaces[namespace].before(socket, eventName, params);
		}

		if (methodToCall.constructor && methodToCall.constructor.name === 'AsyncFunction') {
			const result = await methodToCall(socket, params);
			callback(null, result);
		} else {
			methodToCall(socket, params, (err, result) => {
				callback(err ? { message: err.message } : null, result);
			});
		}
	} catch (err) {
		winston.error(`${eventName}\n${err.stack ? err.stack : err.message}`);
		callback({ message: err.message });
	}
}

async function onMessage(socket, payload) {
	if (!payload.data.length) {
		return winston.warn('[socket.io] Empty payload');
	}

	const eventName = payload.data[0];
	const params = typeof payload.data[1] === 'function' ? {} : payload.data[1];
	const callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function () {};

	if (!eventName) {
		return winston.warn('[socket.io] Empty method name');
	}

	if (typeof eventName !== 'string') {
		const escapedName = validator.escape(String(eventName));
		return callback({ message: `[[error:invalid-event, ${escapedName}]]` });
	}

	const parts = eventName.split('.');
	const namespace = parts[0];
	const methodToCall = parts.reduce((prev, cur) => {
		if (prev !== null && prev[cur] && (!prev.hasOwnProperty || prev.hasOwnProperty(cur))) {
			return prev[cur];
		}
		return null;
	}, Namespaces);

	if (!methodToCall || typeof methodToCall !== 'function') {
		if (process.env.NODE_ENV === 'development') {
			winston.warn(`[socket.io] Unrecognized message: ${eventName}`);
		}
		const escapedName = validator.escape(String(eventName));
		return callback({ message: `[[error:invalid-event, ${escapedName}]]` });
	}

	socket.previousEvents = socket.previousEvents || [];
	socket.previousEvents.push(eventName);
	if (socket.previousEvents.length > 20) {
		socket.previousEvents.shift();
	}

	if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) {
		winston.warn(`[socket.io] Too many emits! Disconnecting uid : ${socket.uid}. Events : ${socket.previousEvents}`);
		return socket.disconnect();
	}

	try {
		await checkMaintenance(socket);
		await validateSession(socket, '[[error:revalidate-failure]]');

		if (Namespaces[namespace].before) {
			await Namespaces[namespace].before(socket, eventName, params);
		}

		if (methodToCall.constructor && methodToCall.constructor.name === 'AsyncFunction') {
			const result = await methodToCall(socket, params);
			callback(null, result);
		} else {
			methodToCall(socket, params, (err, result) => {
				callback(err ? { message: err.message } : null, result);
			});
		}
	} catch (err) {
		winston.error(`${eventName}\n${err.stack ? err.stack : err.message}`);
		callback({ message: err.message });
	}
}

--- a/src/socket.io/index.js
+++ b/src/socket.io/index.js
@@ -11,7 +11,12 @@
 		return winston.warn('[socket.io] Empty method name');
 	}

-	const parts = eventName.toString().split('.');
+	if (typeof eventName !== 'string') {
+		const escapedName = validator.escape(String(eventName));
+		return callback({ message: `[[error:invalid-event, ${escapedName}]]` });
+	}
+
+	const parts = eventName.split('.');
 	const namespace = parts[0];
 	const methodToCall = parts.reduce((prev, cur) => {
 		if (prev !== null && prev[cur] && (!prev.hasOwnProperty || prev.hasOwnProperty(cur))) {


Bazarr manages and downloads subtitles. Prior to 1.3.1, Bazarr contains an arbitrary file read in /system/backup/download/ endpoint in bazarr/app/ui.py does not validate the user-controlled filename variable and uses it in the send_file function, which leads to an arbitrary file read on the system. This issue is fixed in version 1.3.1.

def backup_download(filename):
    return send_file(os.path.join(settings.backup.folder, filename), max_age=0, as_attachment=True)

def swaggerui_static(filename):
    return send_file(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'libs', 'flask_restx',
                     'static', filename))

def proxy(protocol, url):
    url = protocol + '://' + unquote(url)
    params = request.args
    try:
        result = requests.get(url, params, allow_redirects=False, verify=False, timeout=5, headers=headers)
    except Exception as e:
        return dict(status=False, error=repr(e))
    else:
        if result.status_code == 200:
            try:
                version = result.json()['version']
                return dict(status=True, version=version)
            except Exception:
                return dict(status=False, error='Error Occurred. Check your settings.')
        elif result.status_code == 401:
            return dict(status=False, error='Access Denied. Check API key.')
        elif result.status_code == 404:
            return dict(status=False, error='Cannot get version. Maybe unsupported legacy API call?')
        elif 300 <= result.status_code <= 399:
            return dict(status=False, error='Wrong URL Base.')
        else:
            return dict(status=False, error=result.raise_for_status())

def backup_download(filename):
    fullpath = os.path.normpath(os.path.join(settings.backup.folder, filename))
    if not fullpath.startswith(settings.backup.folder):
        return '', 404
    else:
        return send_file(fullpath, max_age=0, as_attachment=True)

def swaggerui_static(filename):
    basepath = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'libs', 'flask_restx',
                            'static')
    fullpath = os.path.join(basepath, filename)
    if not fullpath.startswith(basepath):
        return '', 404
    else:
        return send_file(fullpath)

def proxy(protocol, url):
    if protocol.lower not in ['http', 'https']:
        return dict(status=False, error='Unsupported protocol')
    url = protocol + '://' + unquote(url)
    params = request.args
    try:
        result = requests.get(url, params, allow_redirects=False, verify=False, timeout=5, headers=headers)
    except Exception as e:
        return dict(status=False, error=repr(e))
    else:
        if result.status_code == 200:
            try:
                version = result.json()['version']
                return dict(status=True, version=version)
            except Exception:
                return dict(status=False, error='Error Occurred. Check your settings.')
        elif result.status_code == 401:
            return dict(status=False, error='Access Denied. Check API key.')
        elif result.status_code == 404:
            return dict(status=False, error='Cannot get version. Maybe unsupported legacy API call?')
        elif 300 <= result.status_code <= 399:
            return dict(status=False, error='Wrong URL Base.')
        else:
            return dict(status=False, error=result.raise_for_status())

--- a/bazarr/app/ui.py
+++ b/bazarr/app/ui.py
@@ -1,2 +1,6 @@
 def backup_download(filename):
-    return send_file(os.path.join(settings.backup.folder, filename), max_age=0, as_attachment=True)
+    fullpath = os.path.normpath(os.path.join(settings.backup.folder, filename))
+    if not fullpath.startswith(settings.backup.folder):
+        return '', 404
+    else:
+        return send_file(fullpath, max_age=0, as_attachment=True)

--- a/bazarr/app/ui.py
+++ b/bazarr/app/ui.py
@@ -1,3 +1,8 @@
 def swaggerui_static(filename):
-    return send_file(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'libs', 'flask_restx',
-                     'static', filename))
+    basepath = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'libs', 'flask_restx',
+                            'static')
+    fullpath = os.path.join(basepath, filename)
+    if not fullpath.startswith(basepath):
+        return '', 404
+    else:
+        return send_file(fullpath)

--- a/bazarr/app/ui.py
+++ b/bazarr/app/ui.py
@@ -1,4 +1,6 @@
 def proxy(protocol, url):
+    if protocol.lower not in ['http', 'https']:
+        return dict(status=False, error='Unsupported protocol')
     url = protocol + '://' + unquote(url)
     params = request.args
     try:


Lack of input validation in pdf-image npm package version <= 2.0.0 may allow an attacker to run arbitrary code if PDF file path is constructed based on untrusted user input.

Improper Input Validation

The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.

  constructGetInfoCommand: function () {
    return util.format(
      "pdfinfo \"%s\"",
      this.pdfFilePath
    );
  },

  getInfo: function () {
    var self = this;
    var getInfoCommand = this.constructGetInfoCommand();
    var promise = new Promise(function (resolve, reject) {
      exec(getInfoCommand, function (err, stdout, stderr) {
        if (err) {
          return reject({
            message: "Failed to get PDF'S information",
            error: err,
            stdout: stdout,
            stderr: stderr
          });
        }
        return resolve(self.parseGetInfoCommandOutput(stdout));
      });
    });
    return promise;
  },

  constructGetInfoCommand: function () {
    return {
      cmd: "pdfinfo",
      args: [this.pdfFilePath]
    };
  },

  getInfo: function () {
    var self = this;
    var getInfoCommand = this.constructGetInfoCommand();
    return new Promise(function (resolve, reject) {
      spawn(getInfoCommand.cmd, getInfoCommand.args, { capture: [ 'stdout', 'stderr' ]})
        .then(function (cmdResult) {
          resolve(self.parseGetInfoCommandOutput(cmdResult.stdout.toString()));
        }).catch(reject);
    });
  },

--- a/index.js
+++ b/index.js
@@ -1,6 +1,6 @@
   constructGetInfoCommand: function () {
-    return util.format(
-      "pdfinfo \"%s\"",
-      this.pdfFilePath
-    );
+    return {
+      cmd: "pdfinfo",
+      args: [this.pdfFilePath]
+    };
   },

--- a/index.js
+++ b/index.js
@@ -1,18 +1,10 @@
   getInfo: function () {
     var self = this;
     var getInfoCommand = this.constructGetInfoCommand();
-    var promise = new Promise(function (resolve, reject) {
-      exec(getInfoCommand, function (err, stdout, stderr) {
-        if (err) {
-          return reject({
-            message: "Failed to get PDF'S information",
-            error: err,
-            stdout: stdout,
-            stderr: stderr
-          });
-        }
-        return resolve(self.parseGetInfoCommandOutput(stdout));
-      });
+    return new Promise(function (resolve, reject) {
+      spawn(getInfoCommand.cmd, getInfoCommand.args, { capture: [ 'stdout', 'stderr' ]})
+        .then(function (cmdResult) {
+          resolve(self.parseGetInfoCommandOutput(cmdResult.stdout.toString()));
+        }).catch(reject);
     });
-    return promise;
   },

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

cd /workspace/node-pdf-image
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
jest ./poc


exec.CommandContext in Chaosblade 0.3 through 1.7.3, when server mode is used, allows OS command execution via the cmd parameter without authentication.

func CmdInit() *baseCommand {
	cli := NewCli()
	baseCmd := &baseCommand{
		command: cli.rootCmd,
	}
	// add version command
	baseCmd.AddCommand(&VersionCommand{})
	// add prepare command
	prepareCommand := &PrepareCommand{}
	baseCmd.AddCommand(prepareCommand)
	prepareCommand.AddCommand(&PrepareJvmCommand{})
	prepareCommand.AddCommand(&PrepareCPlusCommand{})

	// add revoke command
	baseCmd.AddCommand(&RevokeCommand{})

	// add create command
	createCommand := &CreateCommand{}
	baseCmd.AddCommand(createCommand)

	// add destroy command
	destroyCommand := &DestroyCommand{}
	baseCmd.AddCommand(destroyCommand)

	// add status command
	baseCmd.AddCommand(&StatusCommand{})

	// add query command
	queryCommand := &QueryCommand{}
	baseCmd.AddCommand(queryCommand)
	queryCommand.AddCommand(&QueryDiskCommand{})
	queryCommand.AddCommand(&QueryNetworkCommand{})
	queryCommand.AddCommand(&QueryJvmCommand{})
	queryCommand.AddCommand(&QueryK8sCommand{})

	// add server command
	serverCommand := &ServerCommand{}
	baseCmd.AddCommand(serverCommand)
	serverCommand.AddCommand(&StartServerCommand{})
	serverCommand.AddCommand(&StopServerCommand{})
	serverCommand.AddCommand(&StatusServerCommand{})

	// add check command
	checkCommand := &CheckCommand{}
	baseCmd.AddCommand(checkCommand)
	checkCommand.AddCommand(&CheckJavaCommand{})
	checkCommand.AddCommand(&CheckOsCommand{})

	return baseCmd
}

func (ssc *StartServerCommand) start0() {
	go func() {
		err := http.ListenAndServe(ssc.ip+":"+ssc.port, nil)
		if err != nil {
			log.Errorf(context.Background(),"start blade server error, %v", err)
			//log.Error(err, "start blade server error")
			os.Exit(1)
		}
	}()
	Register("/chaosblade")
	util.Hold()
}

func Register(requestPath string) {
	http.HandleFunc(requestPath, func(writer http.ResponseWriter, request *http.Request) {
		err := request.ParseForm()
		if err != nil {
			fmt.Fprintf(writer, spec.ReturnFail(spec.ParameterRequestFailed, err.Error()).Print())
			return
		}
		cmds := request.Form["cmd"]
		if len(cmds) != 1 {
			fmt.Fprintf(writer, spec.ResponseFailWithFlags(spec.ParameterLess, "cmd").Print())
			return
		}
		ctx := context.WithValue(context.Background(), "mode", "server")
		response := channel.NewLocalChannel().Run(ctx, path.Join(util.GetProgramPath(), "blade"), cmds[0])
		log.Debugf(ctx, "Server response: %v", response)
		fmt.Fprintf(writer, response.Print())
	})
}

func CmdInit() *baseCommand {
	cli := NewCli()
	baseCmd := &baseCommand{
		command: cli.rootCmd,
	}
	// add version command
	baseCmd.AddCommand(&VersionCommand{})
	// add prepare command
	prepareCommand := &PrepareCommand{}
	baseCmd.AddCommand(prepareCommand)
	prepareCommand.AddCommand(&PrepareJvmCommand{})
	prepareCommand.AddCommand(&PrepareCPlusCommand{})

	// add revoke command
	baseCmd.AddCommand(&RevokeCommand{})

	// add create command
	createCommand := &CreateCommand{}
	baseCmd.AddCommand(createCommand)

	// add destroy command
	destroyCommand := &DestroyCommand{}
	baseCmd.AddCommand(destroyCommand)

	// add status command
	baseCmd.AddCommand(&StatusCommand{})

	// add query command
	queryCommand := &QueryCommand{}
	baseCmd.AddCommand(queryCommand)
	queryCommand.AddCommand(&QueryDiskCommand{})
	queryCommand.AddCommand(&QueryNetworkCommand{})
	queryCommand.AddCommand(&QueryJvmCommand{})
	queryCommand.AddCommand(&QueryK8sCommand{})

	// UPDATE 2023-12-30 Disable server command mode.

	// add check command
	checkCommand := &CheckCommand{}
	baseCmd.AddCommand(checkCommand)
	checkCommand.AddCommand(&CheckJavaCommand{})
	checkCommand.AddCommand(&CheckOsCommand{})

	return baseCmd
}

func (ssc *StartServerCommand) start0() {
	go func() {
		err := http.ListenAndServe(ssc.ip+":"+ssc.port, nil)
		if err != nil {
			log.Errorf(context.Background(), "start blade server error, %v", err)
			//log.Error(err, "start blade server error")
			os.Exit(1)
		}
	}()
	Register("/chaosblade")
	util.Hold()
}

func Register(requestPath string) {
	http.HandleFunc(requestPath, func(writer http.ResponseWriter, request *http.Request) {
		fmt.Fprintf(writer, spec.ReturnFail(spec.CommandIllegal, "Server mode is disabled").Print())
	})
}

--- a/cli/cmd/cmd.go
+++ b/cli/cmd/cmd.go
@@ -33,12 +33,7 @@
 	queryCommand.AddCommand(&QueryJvmCommand{})
 	queryCommand.AddCommand(&QueryK8sCommand{})

-	// add server command
-	serverCommand := &ServerCommand{}
-	baseCmd.AddCommand(serverCommand)
-	serverCommand.AddCommand(&StartServerCommand{})
-	serverCommand.AddCommand(&StopServerCommand{})
-	serverCommand.AddCommand(&StatusServerCommand{})
+	// UPDATE 2023-12-30 Disable server command mode.

 	// add check command
 	checkCommand := &CheckCommand{}

--- a/cli/cmd/server_start.go
+++ b/cli/cmd/server_start.go
@@ -2,7 +2,7 @@
 	go func() {
 		err := http.ListenAndServe(ssc.ip+":"+ssc.port, nil)
 		if err != nil {
-			log.Errorf(context.Background(),"start blade server error, %v", err)
+			log.Errorf(context.Background(), "start blade server error, %v", err)
 			//log.Error(err, "start blade server error")
 			os.Exit(1)
 		}

--- a/cli/cmd/server_start.go
+++ b/cli/cmd/server_start.go
@@ -1,18 +1,5 @@
 func Register(requestPath string) {
 	http.HandleFunc(requestPath, func(writer http.ResponseWriter, request *http.Request) {
-		err := request.ParseForm()
-		if err != nil {
-			fmt.Fprintf(writer, spec.ReturnFail(spec.ParameterRequestFailed, err.Error()).Print())
-			return
-		}
-		cmds := request.Form["cmd"]
-		if len(cmds) != 1 {
-			fmt.Fprintf(writer, spec.ResponseFailWithFlags(spec.ParameterLess, "cmd").Print())
-			return
-		}
-		ctx := context.WithValue(context.Background(), "mode", "server")
-		response := channel.NewLocalChannel().Run(ctx, path.Join(util.GetProgramPath(), "blade"), cmds[0])
-		log.Debugf(ctx, "Server response: %v", response)
-		fmt.Fprintf(writer, response.Print())
+		fmt.Fprintf(writer, spec.ReturnFail(spec.CommandIllegal, "Server mode is disabled").Print())
 	})
 }


Vela is a Pipeline Automation (CI/CD) framework built on Linux container technology written in Golang. In Vela compiler before version 0.6.1 there is a vulnerability which allows exposure of server configuration. It impacts all users of Vela. An attacker can use Sprig's `env` function to retrieve configuration information, see referenced GHSA for an example. This has been fixed in version 0.6.1. In addition to upgrading, it is recommended to rotate all secrets.

func Render(tmpl string, s *types.Step) (types.StepSlice, error) {
	buffer := new(bytes.Buffer)
	config := new(types.Build)

	velaFuncs := funcHandler{envs: convertPlatformVars(s.Environment)}
	templateFuncMap := map[string]interface{}{
		"vela": velaFuncs.returnPlatformVar,
	}

	// parse the template with Masterminds/sprig functions
	//
	// https://pkg.go.dev/github.com/Masterminds/sprig?tab=doc#TxtFuncMap
	t, err := template.New(s.Name).Funcs(sprig.TxtFuncMap()).Funcs(templateFuncMap).Parse(tmpl)
	if err != nil {
		return types.StepSlice{}, fmt.Errorf("unable to parse template %s: %v", s.Template.Name, err)
	}

	// apply the variables to the parsed template
	err = t.Execute(buffer, s.Template.Variables)
	if err != nil {
		return types.StepSlice{}, fmt.Errorf("unable to execute template %s: %v", s.Template.Name, err)
	}

	// unmarshal the template to the pipeline
	err = yaml.Unmarshal(buffer.Bytes(), config)
	if err != nil {
		return types.StepSlice{}, fmt.Errorf("unable to unmarshal yaml: %v", err)
	}

	// ensure all templated steps have template prefix
	for index, newStep := range config.Steps {
		config.Steps[index].Name = fmt.Sprintf("%s_%s", s.Name, newStep.Name)
	}

	return config.Steps, nil
}

func Render(tmpl string, s *types.Step) (types.StepSlice, error) {
	buffer := new(bytes.Buffer)
	config := new(types.Build)

	velaFuncs := funcHandler{envs: convertPlatformVars(s.Environment)}
	templateFuncMap := map[string]interface{}{
		"vela": velaFuncs.returnPlatformVar,
	}
	// modify Masterminds/sprig functions
	// to remove OS functions
	//
	// https://masterminds.github.io/sprig/os.html
	sf := sprig.TxtFuncMap()
	delete(sf, "env")
	delete(sf, "expandenv")

	// parse the template with Masterminds/sprig functions
	//
	// https://pkg.go.dev/github.com/Masterminds/sprig?tab=doc#TxtFuncMap
	t, err := template.New(s.Name).Funcs(sf).Funcs(templateFuncMap).Parse(tmpl)
	if err != nil {
		return types.StepSlice{}, fmt.Errorf("unable to parse template %s: %v", s.Template.Name, err)
	}

	// apply the variables to the parsed template
	err = t.Execute(buffer, s.Template.Variables)
	if err != nil {
		return types.StepSlice{}, fmt.Errorf("unable to execute template %s: %v", s.Template.Name, err)
	}

	// unmarshal the template to the pipeline
	err = yaml.Unmarshal(buffer.Bytes(), config)
	if err != nil {
		return types.StepSlice{}, fmt.Errorf("unable to unmarshal yaml: %v", err)
	}

	// ensure all templated steps have template prefix
	for index, newStep := range config.Steps {
		config.Steps[index].Name = fmt.Sprintf("%s_%s", s.Name, newStep.Name)
	}

	return config.Steps, nil
}

--- a/template/native/render.go
+++ b/template/native/render.go
@@ -6,11 +6,18 @@
 	templateFuncMap := map[string]interface{}{
 		"vela": velaFuncs.returnPlatformVar,
 	}
+	// modify Masterminds/sprig functions
+	// to remove OS functions
+	//
+	// https://masterminds.github.io/sprig/os.html
+	sf := sprig.TxtFuncMap()
+	delete(sf, "env")
+	delete(sf, "expandenv")

 	// parse the template with Masterminds/sprig functions
 	//
 	// https://pkg.go.dev/github.com/Masterminds/sprig?tab=doc#TxtFuncMap
-	t, err := template.New(s.Name).Funcs(sprig.TxtFuncMap()).Funcs(templateFuncMap).Parse(tmpl)
+	t, err := template.New(s.Name).Funcs(sf).Funcs(templateFuncMap).Parse(tmpl)
 	if err != nil {
 		return types.StepSlice{}, fmt.Errorf("unable to parse template %s: %v", s.Template.Name, err)
 	}

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

cd /workspace/compiler
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
go test  -timeout 30s -run ^TestNative_Render_DisallowedFunc_Env$ github.com/go-vela/compiler/template/native

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

cd /workspace/compiler
git apply --whitespace=nowarn /workspace/fix.patch
go test -timeout 30s -run '^(TestNative_Render)$' github.com/go-vela/compiler/template/native


Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. Versions starting with 2.5.0-rc1 and above, prior to 2.5.8, and version 2.6.0-rc4, are vulnerable to an authorization bypass bug which allows a malicious Argo CD user to deploy Applications outside the configured allowed namespaces. Reconciled Application namespaces are specified as a comma-delimited list of glob patterns. When sharding is enabled on the Application controller, it does not enforce that list of patterns when reconciling Applications. For example, if Application namespaces are configured to be argocd-*, the Application controller may reconcile an Application installed in a namespace called other, even though it does not start with argocd-. Reconciliation of the out-of-bounds Application is only triggered when the Application is updated, so the attacker must be able to cause an update operation on the Application resource. This bug only applies to users who have explicitly enabled the "apps-in-any-namespace" feature by setting `application.namespaces` in the argocd-cmd-params-cm ConfigMap or otherwise setting the `--application-namespaces` flags on the Application controller and API server components. The apps-in-any-namespace feature is in beta as of this Security Advisory's publish date. The bug is also limited to Argo CD instances where sharding is enabled by increasing the `replicas` count for the Application controller. Finally, the AppProjects' `sourceNamespaces` field acts as a secondary check against this exploit. To cause reconciliation of an Application in an out-of-bounds namespace, an AppProject must be available which permits Applications in the out-of-bounds namespace. A patch for this vulnerability has been released in versions 2.5.8 and 2.6.0-rc5. As a workaround, running only one replica of the Application controller will prevent exploitation of this bug. Making sure all AppProjects' sourceNamespaces are restricted within the confines of the configured Application namespaces will also prevent exploitation of this bug.

func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
	app, ok := obj.(*appv1.Application)
	if !ok {
		return false
	}
	if ctrl.clusterFilter != nil {
		cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server)
		if err != nil {
			return ctrl.clusterFilter(nil)
		}
		return ctrl.clusterFilter(cluster)
	}

	// Only process given app if it exists in a watched namespace, or in the
	// control plane's namespace.
	if app.Namespace != ctrl.namespace && !glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) {
		return false
	}

	return true
}

func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
	app, ok := obj.(*appv1.Application)
	if !ok {
		return false
	}

	// Only process given app if it exists in a watched namespace, or in the
	// control plane's namespace.
	if app.Namespace != ctrl.namespace && !glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) {
		return false
	}

	if ctrl.clusterFilter != nil {
		cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server)
		if err != nil {
			return ctrl.clusterFilter(nil)
		}
		return ctrl.clusterFilter(cluster)
	}

	return true
}

--- a/./controller/appcontroller.go
+++ b/./controller/appcontroller.go
@@ -3,6 +3,13 @@
 	if !ok {
 		return false
 	}
+
+	// Only process given app if it exists in a watched namespace, or in the
+	// control plane's namespace.
+	if app.Namespace != ctrl.namespace && !glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) {
+		return false
+	}
+
 	if ctrl.clusterFilter != nil {
 		cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server)
 		if err != nil {
@@ -11,11 +18,5 @@
 		return ctrl.clusterFilter(cluster)
 	}

-	// Only process given app if it exists in a watched namespace, or in the
-	// control plane's namespace.
-	if app.Namespace != ctrl.namespace && !glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) {
-		return false
-	}
-
 	return true
 }

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

cd /workspace/argo-cd
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
go test   -timeout 30s -run ^Test_canProcessApp$ github.com/argoproj/argo-cd/v2/controller


The package github.com/argoproj/argo-events/sensors/artifacts before 1.7.1 are vulnerable to Directory Traversal in the (g *GitArtifactReader).Read() API in git.go. This could allow arbitrary file reads if the GitArtifactReader is provided a pathname containing a symbolic link or an implicit directory name such as ...

Improper Link Resolution Before File Access ('Link Following')

The product attempts to access a file based on the filename, but it does not properly prevent that filename from identifying a link or shortcut that resolves to an unintended resource.

var (
	fetchRefSpec = []config.RefSpec{
		"refs/*:refs/*",
		"HEAD:refs/heads/HEAD",
	}
)

func NewGitReader(gitArtifact *v1alpha1.GitArtifact) (*GitArtifactReader, error) {
	return &GitArtifactReader{
		artifact: gitArtifact,
	}, nil
}

func (g *GitArtifactReader) readFromRepository(r *git.Repository, dir string) ([]byte, error) {
	auth, err := g.getGitAuth()
	if err != nil {
		return nil, err
	}

	if g.artifact.Remote != nil {
		_, err := r.CreateRemote(&config.RemoteConfig{
			Name: g.artifact.Remote.Name,
			URLs: g.artifact.Remote.URLS,
		})
		if err != nil {
			return nil, fmt.Errorf("failed to create remote. err: %+v", err)
		}

		fetchOptions := &git.FetchOptions{
			RemoteName: g.artifact.Remote.Name,
			RefSpecs:   fetchRefSpec,
			Force:      true,
		}
		if auth != nil {
			fetchOptions.Auth = auth
		}

		if err := r.Fetch(fetchOptions); err != nil {
			return nil, fmt.Errorf("failed to fetch remote %s. err: %+v", g.artifact.Remote.Name, err)
		}
	}

	w, err := r.Worktree()
	if err != nil {
		return nil, fmt.Errorf("failed to get working tree. err: %+v", err)
	}

	fetchOptions := &git.FetchOptions{
		RemoteName: g.getRemote(),
		RefSpecs:   fetchRefSpec,
		Force:      true,
	}
	if auth != nil {
		fetchOptions.Auth = auth
	}

	// In the case of a specific given ref, it isn't necessary to fetch anything
	// but the single ref
	if g.artifact.Ref != "" {
		fetchOptions.Depth = 1
		fetchOptions.RefSpecs = []config.RefSpec{config.RefSpec(g.artifact.Ref + ":" + g.artifact.Ref)}
	}

	if err := r.Fetch(fetchOptions); err != nil && err != git.NoErrAlreadyUpToDate {
		return nil, fmt.Errorf("failed to fetch. err: %v", err)
	}

	if err := w.Checkout(g.getBranchOrTag()); err != nil {
		return nil, fmt.Errorf("failed to checkout. err: %+v", err)
	}

	// In the case of a specific given ref, it shouldn't be necessary to pull
	if g.artifact.Ref != "" {
		pullOpts := &git.PullOptions{
			RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
			ReferenceName:     g.getBranchOrTag().Branch,
			Force:             true,
		}
		if auth != nil {
			pullOpts.Auth = auth
		}

		if err := w.Pull(pullOpts); err != nil && err != git.NoErrAlreadyUpToDate {
			return nil, fmt.Errorf("failed to pull latest updates. err: %+v", err)
		}
	}

	return ioutil.ReadFile(fmt.Sprintf("%s/%s", dir, g.artifact.FilePath))
}

var (
	fetchRefSpec = []config.RefSpec{
		"refs/*:refs/*",
		"HEAD:refs/heads/HEAD",
	}

	notAllowedInPath = []string{"..", "~", "\\"}
)

func NewGitReader(gitArtifact *v1alpha1.GitArtifact) (*GitArtifactReader, error) {
	if gitArtifact == nil {
		return nil, fmt.Errorf("nil git artifact")
	}
	for _, na := range notAllowedInPath {
		if strings.Contains(gitArtifact.FilePath, na) {
			return nil, fmt.Errorf("%q is not allowed in the filepath", na)
		}
	}

	return &GitArtifactReader{
		artifact: gitArtifact,
	}, nil
}

func (g *GitArtifactReader) readFromRepository(r *git.Repository, dir string) ([]byte, error) {
	auth, err := g.getGitAuth()
	if err != nil {
		return nil, err
	}

	if g.artifact.Remote != nil {
		_, err := r.CreateRemote(&config.RemoteConfig{
			Name: g.artifact.Remote.Name,
			URLs: g.artifact.Remote.URLS,
		})
		if err != nil {
			return nil, fmt.Errorf("failed to create remote. err: %+v", err)
		}

		fetchOptions := &git.FetchOptions{
			RemoteName: g.artifact.Remote.Name,
			RefSpecs:   fetchRefSpec,
			Force:      true,
		}
		if auth != nil {
			fetchOptions.Auth = auth
		}

		if err := r.Fetch(fetchOptions); err != nil {
			return nil, fmt.Errorf("failed to fetch remote %s. err: %+v", g.artifact.Remote.Name, err)
		}
	}

	w, err := r.Worktree()
	if err != nil {
		return nil, fmt.Errorf("failed to get working tree. err: %+v", err)
	}

	fetchOptions := &git.FetchOptions{
		RemoteName: g.getRemote(),
		RefSpecs:   fetchRefSpec,
		Force:      true,
	}
	if auth != nil {
		fetchOptions.Auth = auth
	}

	// In the case of a specific given ref, it isn't necessary to fetch anything
	// but the single ref
	if g.artifact.Ref != "" {
		fetchOptions.Depth = 1
		fetchOptions.RefSpecs = []config.RefSpec{config.RefSpec(g.artifact.Ref + ":" + g.artifact.Ref)}
	}

	if err := r.Fetch(fetchOptions); err != nil && err != git.NoErrAlreadyUpToDate {
		return nil, fmt.Errorf("failed to fetch. err: %v", err)
	}

	if err := w.Checkout(g.getBranchOrTag()); err != nil {
		return nil, fmt.Errorf("failed to checkout. err: %+v", err)
	}

	// In the case of a specific given ref, it shouldn't be necessary to pull
	if g.artifact.Ref != "" {
		pullOpts := &git.PullOptions{
			RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
			ReferenceName:     g.getBranchOrTag().Branch,
			Force:             true,
		}
		if auth != nil {
			pullOpts.Auth = auth
		}

		if err := w.Pull(pullOpts); err != nil && err != git.NoErrAlreadyUpToDate {
			return nil, fmt.Errorf("failed to pull latest updates. err: %+v", err)
		}
	}
	filePath := fmt.Sprintf("%s/%s", dir, g.artifact.FilePath)
	// symbol link is not allowed due to security concern
	isSymbolLink, err := isSymbolLink(filePath)
	if err != nil {
		return nil, err
	}
	if isSymbolLink {
		return nil, fmt.Errorf("%q is a symbol link which is not allowed", g.artifact.FilePath)
	}
	return ioutil.ReadFile(filePath)
}

func isSymbolLink(filepath string) (bool, error) {
	fi, err := os.Lstat(path.Clean(filepath))
	if err != nil {
		return false, err
	}
	if fi.Mode()&os.ModeSymlink != 0 {
		return true, nil
	}
	return false, nil
}

--- a/sensors/artifacts/git.go
+++ b/sensors/artifacts/git.go
@@ -3,4 +3,6 @@
 		"refs/*:refs/*",
 		"HEAD:refs/heads/HEAD",
 	}
+
+	notAllowedInPath = []string{"..", "~", "\\"}
 )

--- a/sensors/artifacts/git.go
+++ b/sensors/artifacts/git.go
@@ -1,4 +1,13 @@
 func NewGitReader(gitArtifact *v1alpha1.GitArtifact) (*GitArtifactReader, error) {
+	if gitArtifact == nil {
+		return nil, fmt.Errorf("nil git artifact")
+	}
+	for _, na := range notAllowedInPath {
+		if strings.Contains(gitArtifact.FilePath, na) {
+			return nil, fmt.Errorf("%q is not allowed in the filepath", na)
+		}
+	}
+
 	return &GitArtifactReader{
 		artifact: gitArtifact,
 	}, nil

--- a/sensors/artifacts/git.go
+++ b/sensors/artifacts/git.go
@@ -71,6 +71,14 @@
 			return nil, fmt.Errorf("failed to pull latest updates. err: %+v", err)
 		}
 	}
-
-	return ioutil.ReadFile(fmt.Sprintf("%s/%s", dir, g.artifact.FilePath))
+	filePath := fmt.Sprintf("%s/%s", dir, g.artifact.FilePath)
+	// symbol link is not allowed due to security concern
+	isSymbolLink, err := isSymbolLink(filePath)
+	if err != nil {
+		return nil, err
+	}
+	if isSymbolLink {
+		return nil, fmt.Errorf("%q is a symbol link which is not allowed", g.artifact.FilePath)
+	}
+	return ioutil.ReadFile(filePath)
 }

--- /dev/null
+++ b/sensors/artifacts/git.go
@@ -0,0 +1,10 @@
+func isSymbolLink(filepath string) (bool, error) {
+	fi, err := os.Lstat(path.Clean(filepath))
+	if err != nil {
+		return false, err
+	}
+	if fi.Mode()&os.ModeSymlink != 0 {
+		return true, nil
+	}
+	return false, nil
+}


IPython (Interactive Python) is a command shell for interactive computing in multiple programming languages, originally developed for the Python programming language. Versions prior to 8.1.0 are subject to a command injection vulnerability with very specific prerequisites. This vulnerability requires that the function `IPython.utils.terminal.set_term_title` be called on Windows in a Python environment where ctypes is not available. The dependency on `ctypes` in `IPython.utils._process_win32` prevents the vulnerable code from ever being reached in the ipython binary. However, as a library that could be used by another tool `set_term_title` could be called and hence introduce a vulnerability. Should an attacker get untrusted input to an instance of this function they would be able to inject shell commands as current process and limited to the scope of the current process. Users of ipython as a library are advised to upgrade. Users unable to upgrade should ensure that any calls to the `IPython.utils.terminal.set_term_title` function are done with trusted or filtered input.

if os.name == 'posix':
    TERM = os.environ.get('TERM','')
    if TERM.startswith('xterm'):
        _set_term_title = _set_term_title_xterm
        _restore_term_title = _restore_term_title_xterm
elif sys.platform == 'win32':
    try:
        import ctypes

        SetConsoleTitleW = ctypes.windll.kernel32.SetConsoleTitleW
        SetConsoleTitleW.argtypes = [ctypes.c_wchar_p]

        def _set_term_title(title):
            """Set terminal title using ctypes to access the Win32 APIs."""
            SetConsoleTitleW(title)
    except ImportError:
        def _set_term_title(title):
            """Set terminal title using the 'title' command."""
            global ignore_termtitle

            try:
                # Cannot be on network share when issuing system commands
                curr = os.getcwd()
                os.chdir("C:")
                ret = os.system("title " + title)
            finally:
                os.chdir(curr)
            if ret:
                # non-zero return code signals error, don't try again
                ignore_termtitle = True

if os.name == 'posix':
    TERM = os.environ.get('TERM','')
    if TERM.startswith('xterm'):
        _set_term_title = _set_term_title_xterm
        _restore_term_title = _restore_term_title_xterm
elif sys.platform == 'win32':
    import ctypes

    SetConsoleTitleW = ctypes.windll.kernel32.SetConsoleTitleW
    SetConsoleTitleW.argtypes = [ctypes.c_wchar_p]

    def _set_term_title(title):
        """Set terminal title using ctypes to access the Win32 APIs."""
        SetConsoleTitleW(title)

--- a/IPython/utils/terminal.py
+++ b/IPython/utils/terminal.py
@@ -4,27 +4,11 @@
         _set_term_title = _set_term_title_xterm
         _restore_term_title = _restore_term_title_xterm
 elif sys.platform == 'win32':
-    try:
-        import ctypes
+    import ctypes

-        SetConsoleTitleW = ctypes.windll.kernel32.SetConsoleTitleW
-        SetConsoleTitleW.argtypes = [ctypes.c_wchar_p]
-
-        def _set_term_title(title):
-            """Set terminal title using ctypes to access the Win32 APIs."""
-            SetConsoleTitleW(title)
-    except ImportError:
-        def _set_term_title(title):
-            """Set terminal title using the 'title' command."""
-            global ignore_termtitle
+    SetConsoleTitleW = ctypes.windll.kernel32.SetConsoleTitleW
+    SetConsoleTitleW.argtypes = [ctypes.c_wchar_p]

-            try:
-                # Cannot be on network share when issuing system commands
-                curr = os.getcwd()
-                os.chdir("C:")
-                ret = os.system("title " + title)
-            finally:
-                os.chdir(curr)
-            if ret:
-                # non-zero return code signals error, don't try again
-                ignore_termtitle = True
+    def _set_term_title(title):
+        """Set terminal title using ctypes to access the Win32 APIs."""
+        SetConsoleTitleW(title)


Products.isurlinportal is a replacement for isURLInPortal method in Plone. Versions of Products.isurlinportal prior to 1.2.0 have an Open Redirect vulnerability. Various parts of Plone use the 'is url in portal' check for security, mostly to see if it is safe to redirect to a url. A url like `https://example.org` is not in the portal. The url `https:example.org` without slashes is considered to be in the portal. When redirecting, some browsers go to `https://example.org`, others give an error. Attackers may use this to redirect victims to their site, especially as part of a phishing attack. The problem has been patched in Products.isurlinportal 1.2.0.

def isURLInPortal(self, url, context=None):
    # Note: no docstring, because the method is publicly available
    # but does not need to be callable on site-url/portal_url/isURLInPortal.

    # Check if a given url is on the same host and contains the portal
    # path.  Used to ensure that login forms can determine relevant
    # referrers (i.e. in portal).  Also return true for some relative
    # urls if context is passed in to allow for url parsing. When context
    # is not provided, assume that relative urls are in the portal. It is
    # assumed that http://portal is the same portal as https://portal.

    # External sites listed in 'allow_external_login_sites' of
    # site_properties are also considered within the portal to allow for
    # single sign on.

    if len(url.splitlines()) > 1:
        # very fishy
        return False
    if url != url.strip():
        # somewhat fishy
        return False
    if url != " ".join(url.split()):
        # Some non-normal whitespace is used, like a tab.
        # Could be a ploy to circumvent our checks.  We don't trust this.
        return False
    if url and not safe_url_first_char(url):
        return False

    # sanitize url
    url = re.sub("^[\x00-\x20]+", "", url).strip()
    cmp_url = url.lower()
    for bad in BAD_URL_PARTS:
        if bad in cmp_url:
            return False

    p_url = self()

    schema, u_host, u_path, _, _, _ = urlparse(url)
    if schema and schema not in ALLOWED_SCHEMAS:
        # Redirecting to 'data:' may be harmful,
        # and redirecting to 'mailto:' or 'ftp:' is silly.
        return False

    # Someone may be doing tricks with escaped html code.
    unescaped_url = unescape(url)
    if unescaped_url != url:
        if not self.isURLInPortal(unescaped_url):
            return False

    if not u_host and not u_path.startswith("/"):
        if context is None:
            return True  # old behavior
        if not context.isPrincipiaFolderish:
            useurl = context.aq_parent.absolute_url()
        else:
            useurl = context.absolute_url()
    else:
        useurl = p_url  # when u_path.startswith('/')
    if not useurl.endswith("/"):
        useurl += "/"

    # urljoin to current url to get an absolute path
    _, u_host, u_path, _, _, _ = urlparse(urljoin(useurl, url))

    # normalise to end with a '/' so /foobar is not considered within /foo
    if not u_path:
        u_path = "/"
    else:
        u_path = normpath(u_path)
        if not u_path.endswith("/"):
            u_path += "/"
    _, host, path, _, _, _ = urlparse(p_url)
    if not path.endswith("/"):
        path += "/"
    if host == u_host and u_path.startswith(path):
        return True

    for external_site in get_external_sites(self):
        _, host, path, _, _, _ = urlparse(external_site)
        if not path.endswith("/"):
            path += "/"
        if host == u_host and u_path.startswith(path):
            return True
    return False

def isURLInPortal(self, url, context=None):
    # Note: no docstring, because the method is publicly available
    # but does not need to be callable on site-url/portal_url/isURLInPortal.

    # Check if a given url is on the same host and contains the portal
    # path.  Used to ensure that login forms can determine relevant
    # referrers (i.e. in portal).  Also return true for some relative
    # urls if context is passed in to allow for url parsing. When context
    # is not provided, assume that relative urls are in the portal. It is
    # assumed that http://portal is the same portal as https://portal.

    # External sites listed in 'allow_external_login_sites' of
    # site_properties are also considered within the portal to allow for
    # single sign on.

    if len(url.splitlines()) > 1:
        # very fishy
        return False
    if url != url.strip():
        # somewhat fishy
        return False
    if url != " ".join(url.split()):
        # Some non-normal whitespace is used, like a tab.
        # Could be a ploy to circumvent our checks.  We don't trust this.
        return False
    if url and not safe_url_first_char(url):
        return False

    # sanitize url
    url = re.sub("^[\x00-\x20]+", "", url).strip()
    cmp_url = url.lower()
    for bad in BAD_URL_PARTS:
        if bad in cmp_url:
            return False

    p_url = self()

    schema, u_host, u_path, _, _, _ = urlparse(url)
    if schema and schema not in ALLOWED_SCHEMAS:
        # Redirecting to 'data:' may be harmful,
        # and redirecting to 'mailto:' or 'ftp:' is silly.
        return False

    if schema and not u_host:
        # Example: https:example.org
        # When we redirect to this, some browsers fail, others happily go to example.org.
        # In any case, this is not in the portal.
        return False

    # Someone may be doing tricks with escaped html code.
    unescaped_url = unescape(url)
    if unescaped_url != url:
        if not self.isURLInPortal(unescaped_url):
            return False

    if not u_host and not u_path.startswith("/"):
        if context is None:
            return True  # old behavior
        if not context.isPrincipiaFolderish:
            useurl = context.aq_parent.absolute_url()
        else:
            useurl = context.absolute_url()
    else:
        useurl = p_url  # when u_path.startswith('/')
    if not useurl.endswith("/"):
        useurl += "/"

    # urljoin to current url to get an absolute path
    _, u_host, u_path, _, _, _ = urlparse(urljoin(useurl, url))

    # normalise to end with a '/' so /foobar is not considered within /foo
    if not u_path:
        u_path = "/"
    else:
        u_path = normpath(u_path)
        if not u_path.endswith("/"):
            u_path += "/"
    _, host, path, _, _, _ = urlparse(p_url)
    if not path.endswith("/"):
        path += "/"
    if host == u_host and u_path.startswith(path):
        return True

    for external_site in get_external_sites(self):
        _, host, path, _, _, _ = urlparse(external_site)
        if not path.endswith("/"):
            path += "/"
        if host == u_host and u_path.startswith(path):
            return True
    return False

--- a/Products/isurlinportal/__init__.py
+++ b/Products/isurlinportal/__init__.py
@@ -41,6 +41,12 @@
         # and redirecting to 'mailto:' or 'ftp:' is silly.
         return False

+    if schema and not u_host:
+        # Example: https:example.org
+        # When we redirect to this, some browsers fail, others happily go to example.org.
+        # In any case, this is not in the portal.
+        return False
+
     # Someone may be doing tricks with escaped html code.
     unescaped_url = unescape(url)
     if unescaped_url != url:


In lunary-ai/lunary version 1.2.2, an incorrect synchronization vulnerability allows unprivileged users to rename projects they do not have access to. Specifically, an unprivileged user can send a PATCH request to the project's endpoint with a new name for a project, despite not having the necessary permissions or being assigned to the project. This issue allows for unauthorized modification of project names, potentially leading to confusion or unauthorized access to project resources.

Authorization Bypass Through User-Controlled Key

The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.

export async function checkProjectAccess(projectId: string, userId: string) {
  const [{ exists: hasAccess }] = await sql`
      select exists (
        select 1
        from project
        where org_id = (select org_id from account where id = ${userId})
          and id = ${projectId}
      )
    `
  return hasAccess
}

export async function checkProjectAccess(projectId: string, userId: string) {
  const [{ exists: hasAccess }] = await sql`
      select exists (
        select 1
        from account_project ap
        where ap.project_id = ${projectId} and ap.account_id = ${userId}
      )
  `
  return hasAccess
}

--- a/packages/backend/src/utils/authorization.ts
+++ b/packages/backend/src/utils/authorization.ts
@@ -2,10 +2,9 @@
   const [{ exists: hasAccess }] = await sql`
       select exists (
         select 1
-        from project
-        where org_id = (select org_id from account where id = ${userId})
-          and id = ${projectId}
+        from account_project ap
+        where ap.project_id = ${projectId} and ap.account_id = ${userId}
       )
-    `
+  `
   return hasAccess
 }