Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. "Local sync" is an Argo CD feature that allows developers to temporarily override an Application's manifests with locally-defined manifests. Use of the feature should generally be limited to highly-trusted users, since it allows the user to bypass any merge protections in git. An improper validation bug allows users who have `create` privileges but not `override` privileges to sync local manifests on app creation. All other restrictions, including AppProject restrictions are still enforced. The only restriction which is not enforced is that the manifests come from some approved git/Helm/OCI source. The bug was introduced in 1.2.0-rc1 when the local manifest sync feature was added. The bug has been patched in Argo CD versions 2.10.3, 2.9.8, and 2.8.12. Users are advised to upgrade. Users unable to upgrade may mitigate the risk of branch protection bypass by removing `applications, create` RBAC access. The only way to eliminate the issue without removing RBAC access is to upgrade to a patched version.

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.

func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateRequest) (*appv1.Application, error) {
	if q.GetApplication() == nil {
		return nil, fmt.Errorf("error creating application: application is nil in request")
	}
	a := q.GetApplication()

	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, a.RBACName(s.ns)); err != nil {
		return nil, err
	}

	s.projectLock.RLock(a.Spec.GetProject())
	defer s.projectLock.RUnlock(a.Spec.GetProject())

	validate := true
	if q.Validate != nil {
		validate = *q.Validate
	}
	err := s.validateAndNormalizeApp(ctx, a, validate)
	if err != nil {
		return nil, fmt.Errorf("error while validating and normalizing app: %w", err)
	}

	appNs := s.appNamespaceOrDefault(a.Namespace)

	if !s.isNamespaceEnabled(appNs) {
		return nil, security.NamespaceNotPermittedError(appNs)
	}

	created, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Create(ctx, a, metav1.CreateOptions{})
	if err == nil {
		s.logAppEvent(created, ctx, argo.EventReasonResourceCreated, "created application")
		s.waitSync(created)
		return created, nil
	}
	if !apierr.IsAlreadyExists(err) {
		return nil, fmt.Errorf("error creating application: %w", err)
	}

	// act idempotent if existing spec matches new spec
	existing, err := s.appLister.Applications(appNs).Get(a.Name)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "unable to check existing application details (%s): %v", appNs, err)
	}
	equalSpecs := reflect.DeepEqual(existing.Spec, a.Spec) &&
		reflect.DeepEqual(existing.Labels, a.Labels) &&
		reflect.DeepEqual(existing.Annotations, a.Annotations) &&
		reflect.DeepEqual(existing.Finalizers, a.Finalizers)

	if equalSpecs {
		return existing, nil
	}
	if q.Upsert == nil || !*q.Upsert {
		return nil, status.Errorf(codes.InvalidArgument, "existing application spec is different, use upsert flag to force update")
	}
	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil {
		return nil, err
	}
	updated, err := s.updateApp(existing, a, ctx, true)
	if err != nil {
		return nil, fmt.Errorf("error updating application: %w", err)
	}
	return updated, nil
}

func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateRequest) (*appv1.Application, error) {
	if q.GetApplication() == nil {
		return nil, fmt.Errorf("error creating application: application is nil in request")
	}
	a := q.GetApplication()

	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, a.RBACName(s.ns)); err != nil {
		return nil, err
	}

	s.projectLock.RLock(a.Spec.GetProject())
	defer s.projectLock.RUnlock(a.Spec.GetProject())

	validate := true
	if q.Validate != nil {
		validate = *q.Validate
	}
	err := s.validateAndNormalizeApp(ctx, a, validate)
	if err != nil {
		return nil, fmt.Errorf("error while validating and normalizing app: %w", err)
	}

	appNs := s.appNamespaceOrDefault(a.Namespace)

	if !s.isNamespaceEnabled(appNs) {
		return nil, security.NamespaceNotPermittedError(appNs)
	}

	// Don't let the app creator set the operation explicitly. Those requests should always go through the Sync API.
	if a.Operation != nil {
		log.WithFields(log.Fields{
			"application":            a.Name,
			argocommon.SecurityField: argocommon.SecurityLow,
		}).Warn("User attempted to set operation on application creation. This could have allowed them to bypass branch protection rules by setting manifests directly. Ignoring the set operation.")
		a.Operation = nil
	}

	created, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Create(ctx, a, metav1.CreateOptions{})
	if err == nil {
		s.logAppEvent(created, ctx, argo.EventReasonResourceCreated, "created application")
		s.waitSync(created)
		return created, nil
	}
	if !apierr.IsAlreadyExists(err) {
		return nil, fmt.Errorf("error creating application: %w", err)
	}

	// act idempotent if existing spec matches new spec
	existing, err := s.appLister.Applications(appNs).Get(a.Name)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "unable to check existing application details (%s): %v", appNs, err)
	}
	equalSpecs := reflect.DeepEqual(existing.Spec, a.Spec) &&
		reflect.DeepEqual(existing.Labels, a.Labels) &&
		reflect.DeepEqual(existing.Annotations, a.Annotations) &&
		reflect.DeepEqual(existing.Finalizers, a.Finalizers)

	if equalSpecs {
		return existing, nil
	}
	if q.Upsert == nil || !*q.Upsert {
		return nil, status.Errorf(codes.InvalidArgument, "existing application spec is different, use upsert flag to force update")
	}
	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil {
		return nil, err
	}
	updated, err := s.updateApp(existing, a, ctx, true)
	if err != nil {
		return nil, fmt.Errorf("error updating application: %w", err)
	}
	return updated, nil
}

--- a/server/application/application.go
+++ b/server/application/application.go
@@ -24,6 +24,15 @@

 	if !s.isNamespaceEnabled(appNs) {
 		return nil, security.NamespaceNotPermittedError(appNs)
+	}
+
+	// Don't let the app creator set the operation explicitly. Those requests should always go through the Sync API.
+	if a.Operation != nil {
+		log.WithFields(log.Fields{
+			"application":            a.Name,
+			argocommon.SecurityField: argocommon.SecurityLow,
+		}).Warn("User attempted to set operation on application creation. This could have allowed them to bypass branch protection rules by setting manifests directly. Ignoring the set operation.")
+		a.Operation = nil
 	}

 	created, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Create(ctx, a, metav1.CreateOptions{})

#!/bin/bash
# From ghcr.io/anonymous2578-data/cve-2023-50726: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 ^TestCreateAppWithOperation$ github.com/argoproj/argo-cd/v2/server/application


iTunesRPC-Remastered is a discord rich presence application for use with iTunes & Apple Music. In code before commit 24f43aa user input is not properly sanitized and code injection is possible. Users are advised to upgrade as soon as is possible. There are no known workarounds for this issue.

Improper Encoding or Escaping of Output

The product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. As a result, the intended structure of the message is not preserved.

import io
import base64
import logging
import random
import ast
from PIL import Image

from flask import Flask, request, jsonify, abort

def uploadimage():
    #print(request.json)
    if not request.json or 'image' not in request.json:
        print("No data sent or no image provided. Aborting with 400.")
        abort(400)

    im_b64 = request.json['image']
    img_bytes = base64.b64decode(im_b64.encode('utf-8'))
    img = Image.open(io.BytesIO(img_bytes))

    file_ending = img.format
    print(f"File has filetype {file_ending}.")

    if file_ending == "JPEG":
        file_ending = ".jpg"
    else:
        file_ending = ".png"

    one_hundred_million = 100000000
    lots_of_nine = 999999999

    file_name = None

    f = open("all_files", "r")
    all_files = ast.literal_eval(f.read())
    f.close()

    attempt = 0

    while file_name == None or file_name in all_files:
        if attempt <= 1000:
            file_name = random.randint(one_hundred_million, lots_of_nine)

            file_name = base64.b64encode(str(file_name).encode("utf-8")).decode("utf-8")

            print(f"Trying new file name: {file_name}")
        else:
            attempt = 0
            one_hundred_million += 100000
            lots_of_nine += 1000000

            while one_hundred_million >= lots_of_nine:
                one_hundred_million -= 10000

            one_hundred_million -= 10000

    print(f"Successful file name: {file_name}")

    title = request.json["title"]
    print("First 9 chars of title: "+str(title[:9]))
    print("Title from 10th char: "+str(title[9::]))
    if title[:9] == "[PAUSED] ":
        title = title[9::]

    singer = request.json["singer"]
    album = request.json["album"]

    file_db_entry = [{"title": title, "singer": singer, "album": album}, file_name, file_ending]
    print(f"New db entry: {file_db_entry}")

    all_files.append(file_db_entry)

    f = open("all_files", "w")
    f.write(str(all_files))
    f.close()

    file_name = file_name + file_ending

    image = img.save(file_name)
    print(f"Saved {file_name} from {file_db_entry}.")
    print(f"Returning {file_db_entry}.")
    return {"entry": file_db_entry}

def get(image_file, domain, title, singer, album):
    import base64
    import json
    import ast

    import requests

    api = f'http://{domain}:7873/bGVhdmVfcmlnaHRfbm93'

    with open(image_file, "rb") as f:
        im_bytes = f.read()
    im_b64 = base64.b64encode(im_bytes).decode("utf8")

    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}

    status = try_get_cached(domain, {"title": title, "singer": singer, "album": album})
    status = ast.literal_eval(str(status))

    if status == None:
        print("Cached version not found. Uploading image with song metadata.")
        payload = json.dumps({"image": im_b64, "title": title, "singer": singer, "album": album})
        response = requests.post(api, data=payload, headers=headers)

        data = response.text["data"]
    else:
        data = status

    # data = [{"title": title, "singer": singer, "album": album}, file_name, file_ending]

    return data

import io
import base64
import logging
import random
import ast
from PIL import Image

from flask import Flask, request, abort
from html import escape

def uploadimage():
    #print(request.json)
    if not request.json or 'image' not in request.json:
        print("No data sent or no image provided. Aborting with 400.")
        abort(400)

    im_b64 = request.json['image']
    img_bytes = base64.b64decode(im_b64.encode('utf-8'))
    img = Image.open(io.BytesIO(img_bytes))

    file_ending = img.format
    print(f"File has filetype {file_ending}.")

    if file_ending == "JPEG":
        file_ending = ".jpg"
    else:
        file_ending = ".png"

    one_hundred_million = 100000000
    lots_of_nine = 999999999

    file_name = None

    f = open("all_files", "r")
    all_files = ast.literal_eval(f.read())
    f.close()

    attempt = 0

    while file_name == None or file_name in all_files:
        if attempt <= 1000:
            file_name = random.randint(one_hundred_million, lots_of_nine)

            file_name = base64.b64encode(str(file_name).encode("utf-8")).decode("utf-8")

            print(f"Trying new file name: {file_name}")
        else:
            attempt = 0
            one_hundred_million += 100000
            lots_of_nine += 1000000

            while one_hundred_million >= lots_of_nine:
                one_hundred_million -= 10000

            one_hundred_million -= 10000

    print(f"Successful file name: {file_name}")

    title = request.json["title"]
    print("First 9 chars of title: "+str(title[:9]))
    print("Title from 10th char: "+str(title[9::]))
    if title[:9] == "[PAUSED] ":
        title = title[9::]

    singer = request.json["singer"]
    album = request.json["album"]

    file_db_entry = [{"title": title, "singer": singer, "album": album}, file_name, file_ending]
    print(f"New db entry: {file_db_entry}")

    all_files.append(file_db_entry)

    f = open("all_files", "w")
    f.write(str(all_files))
    f.close()

    file_name = file_name + file_ending

    image = img.save(file_name)
    print(f"Saved {file_name} from {file_db_entry}.")
    print(f"Returning {file_db_entry}.")
    return escape({"entry": file_db_entry})

def get(image_file, domain, title, singer, album):
    import base64
    import json
    import ast
    from html import unescape

    import requests

    api = f'http://{domain}:7873/bGVhdmVfcmlnaHRfbm93'

    with open(image_file, "rb") as f:
        im_bytes = f.read()
    im_b64 = base64.b64encode(im_bytes).decode("utf8")

    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}

    status = try_get_cached(domain, {"title": title, "singer": singer, "album": album})
    status = ast.literal_eval(str(status))

    if status == None:
        print("Cached version not found. Uploading image with song metadata.")
        payload = json.dumps({"image": im_b64, "title": title, "singer": singer, "album": album})
        response = requests.post(api, data=payload, headers=headers)

        data = unescape(response.text["entry"])
    else:
        data = status

    # data = [{"title": title, "singer": singer, "album": album}, file_name, file_ending]

    return data

--- a/upload/server.py
+++ b/upload/server.py
@@ -5,4 +5,5 @@
 import ast
 from PIL import Image

-from flask import Flask, request, jsonify, abort
+from flask import Flask, request, abort
+from html import escape

--- a/upload/server.py
+++ b/upload/server.py
@@ -69,4 +69,4 @@
     image = img.save(file_name)
     print(f"Saved {file_name} from {file_db_entry}.")
     print(f"Returning {file_db_entry}.")
-    return {"entry": file_db_entry}
+    return escape({"entry": file_db_entry})

--- a/module/connect_to_server.py
+++ b/module/connect_to_server.py
@@ -1,7 +1,8 @@
 def get(image_file, domain, title, singer, album):
     import base64
     import json
-    import ast
+    import ast
+    from html import unescape

     import requests

@@ -21,7 +22,7 @@
         payload = json.dumps({"image": im_b64, "title": title, "singer": singer, "album": album})
         response = requests.post(api, data=payload, headers=headers)

-        data = response.text["data"]
+        data = unescape(response.text["entry"])
     else:
         data = status


OS Command Injection in GitHub repository gogs/gogs prior to 0.12.11.

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.

func isRepositoryGitPath(path string) bool {
	return strings.HasSuffix(path, ".git") ||
		strings.Contains(path, ".git/") ||
		strings.Contains(path, `.git\`) ||
		// Windows treats ".git." the same as ".git"
		strings.HasSuffix(path, ".git.") ||
		strings.Contains(path, ".git./") ||
		strings.Contains(path, `.git.\`)
}

func isRepositoryGitPath(path string) bool {
	path = strings.ToLower(path)
	return strings.HasSuffix(path, ".git") ||
		strings.Contains(path, ".git/") ||
		strings.Contains(path, `.git\`) ||
		// Windows treats ".git." the same as ".git"
		strings.HasSuffix(path, ".git.") ||
		strings.Contains(path, ".git./") ||
		strings.Contains(path, `.git.\`)
}

--- a/internal/db/repo_editor.go
+++ b/internal/db/repo_editor.go
@@ -1,4 +1,5 @@
 func isRepositoryGitPath(path string) bool {
+	path = strings.ToLower(path)
 	return strings.HasSuffix(path, ".git") ||
 		strings.Contains(path, ".git/") ||
 		strings.Contains(path, `.git\`) ||

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

cd /workspace/gogs
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
go test   -timeout 30s -run ^TestIsRepositoryGitPath$ gogs.io/gogs/internal/db

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

cd /workspace/gogs
git apply --whitespace=nowarn /workspace/fix.patch
go test -timeout 30s -run '^(Test_isRepositoryGitPath)$' gogs.io/gogs/internal/db


KubeClarity is a tool for detection and management of Software Bill Of Materials (SBOM) and vulnerabilities of container images and filesystems. A time/boolean SQL Injection is present in the following resource `/api/applicationResources` via the following parameter `packageID`. As it can be seen in backend/pkg/database/id_view.go, while building the SQL Query the `fmt.Sprintf` function is used to build the query string without the input having first been subjected to any validation. This vulnerability is fixed in 2.23.1.

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

The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.

func (i *IDsViewHandler) GetIDs(params GetIDsParams, idsShouldMatch bool) ([]string, error) {
	ids := []string{}

	if len(params.FilterIDs) == 0 {
		// nothing to filter by, so return nil so that the caller knows
		// the difference between a query wasn't made, and there were
		// no results
		return nil, nil
	}

	lookupIDColumnName := getIDViewColumnNameByIDType(params.LookupIDType)

	// we will use Session(&gorm.Session{}) here so every call to the handler will start from scratch
	tx := i.IDsView.
		Session(&gorm.Session{}).
		Select("distinct " + lookupIDColumnName)

	filterIDColumnName := getIDViewColumnNameByIDType(params.FilterIDType)

	if idsShouldMatch {
		for _, id := range params.FilterIDs {
			// for each OR filter we need to verify that lookup id column is not null to avoid failing during Find
			tx.Or(fmt.Sprintf("%s = '%s' AND %s is not null", filterIDColumnName, id,
				lookupIDColumnName))
		}
	} else {
		for _, id := range params.FilterIDs {
			tx.Not(filterIDColumnName+" = ?", id)
		}
		// after we filter out all ids need to verify that lookup id column is not null to avoid failing during Find
		tx.Where(lookupIDColumnName + " is not null")
	}

	// Verify that query will return at least one result.
	var count int64
	if err := tx.Count(&count).Error; err != nil {
		return nil, fmt.Errorf("failed to count IDs: %v", err)
	}
	if count <= 0 {
		// No need to run query, but return empty list of strings
		// because there are no results, not nil
		log.Debugf("no IDs found. pararms=%+v", params)
		return ids, nil
	}

	// Run query.
	if err := tx.Find(&ids).Error; err != nil {
		return nil, fmt.Errorf("failed to get IDs: %v", err)
	}

	return ids, nil
}

func (i *IDsViewHandler) GetIDs(params GetIDsParams, idsShouldMatch bool) ([]string, error) {
	ids := []string{}

	if len(params.FilterIDs) == 0 {
		// nothing to filter by, so return nil so that the caller knows
		// the difference between a query wasn't made, and there were
		// no results
		return nil, nil
	}

	lookupIDColumnName := getIDViewColumnNameByIDType(params.LookupIDType)

	// we will use Session(&gorm.Session{}) here so every call to the handler will start from scratch
	tx := i.IDsView.
		Session(&gorm.Session{}).
		Select("distinct " + lookupIDColumnName)

	filterIDColumnName := getIDViewColumnNameByIDType(params.FilterIDType)

	if idsShouldMatch {
		for _, id := range params.FilterIDs {
			// for each OR filter we need to verify that lookup id column is not null to avoid failing during Find
			tx.Or("? = ? AND ? is not null", filterIDColumnName, id,
				lookupIDColumnName)
		}
	} else {
		for _, id := range params.FilterIDs {
			tx.Not(filterIDColumnName+" = ?", id)
		}
		// after we filter out all ids need to verify that lookup id column is not null to avoid failing during Find
		tx.Where(lookupIDColumnName + " is not null")
	}

	// Verify that query will return at least one result.
	var count int64
	if err := tx.Count(&count).Error; err != nil {
		return nil, fmt.Errorf("failed to count IDs: %v", err)
	}
	if count <= 0 {
		// No need to run query, but return empty list of strings
		// because there are no results, not nil
		log.Debugf("no IDs found. pararms=%+v", params)
		return ids, nil
	}

	// Run query.
	if err := tx.Find(&ids).Error; err != nil {
		return nil, fmt.Errorf("failed to get IDs: %v", err)
	}

	return ids, nil
}

--- a/backend/pkg/database/id_view.go
+++ b/backend/pkg/database/id_view.go
@@ -20,8 +20,8 @@
 	if idsShouldMatch {
 		for _, id := range params.FilterIDs {
 			// for each OR filter we need to verify that lookup id column is not null to avoid failing during Find
-			tx.Or(fmt.Sprintf("%s = '%s' AND %s is not null", filterIDColumnName, id,
-				lookupIDColumnName))
+			tx.Or("? = ? AND ? is not null", filterIDColumnName, id,
+				lookupIDColumnName)
 		}
 	} else {
 		for _, id := range params.FilterIDs {


The Kubernetes kubelet component allows arbitrary command execution via specially crafted gitRepo volumes.This issue affects kubelet: through 1.28.11, from 1.29.0 through 1.29.6, from 1.30.0 through 1.30.2.

External Control of File Name or Path

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

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

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

func validateVolume(src *v1.GitRepoVolumeSource) error {
	if err := validateNonFlagArgument(src.Repository, "repository"); err != nil {
		return err
	}
	if err := validateNonFlagArgument(src.Revision, "revision"); err != nil {
		return err
	}
	if err := validateNonFlagArgument(src.Directory, "directory"); err != nil {
		return err
	}
	return nil
}

func validateVolume(src *v1.GitRepoVolumeSource) error {
	if err := validateNonFlagArgument(src.Repository, "repository"); err != nil {
		return err
	}
	if err := validateNonFlagArgument(src.Revision, "revision"); err != nil {
		return err
	}
	if err := validateNonFlagArgument(src.Directory, "directory"); err != nil {
		return err
	}
	if (src.Revision != "") && (src.Directory != "") {
		cleanedDir := filepath.Clean(src.Directory)
		if strings.Contains(cleanedDir, "/") || (strings.Contains(cleanedDir, "\\")) {
			return fmt.Errorf("%q is not a valid directory, it must not contain a directory separator", src.Directory)
		}
	}
	return nil
}

--- a/pkg/volume/git_repo/git_repo.go
+++ b/pkg/volume/git_repo/git_repo.go
@@ -8,5 +8,11 @@
 	if err := validateNonFlagArgument(src.Directory, "directory"); err != nil {
 		return err
 	}
+	if (src.Revision != "") && (src.Directory != "") {
+		cleanedDir := filepath.Clean(src.Directory)
+		if strings.Contains(cleanedDir, "/") || (strings.Contains(cleanedDir, "\\")) {
+			return fmt.Errorf("%q is not a valid directory, it must not contain a directory separator", src.Directory)
+		}
+	}
 	return nil
 }

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

cd /workspace/kubernetes
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
go test -timeout 30s -run ^TestPlugin$ k8s.io/kubernetes/pkg/volume/git_repo

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

cd /workspace/kubernetes
git apply --whitespace=nowarn /workspace/fix.patch
go test -timeout 30s -run '^(TestPlugin|TestCanSupport)$' k8s.io/kubernetes/pkg/volume/git_repo


Missing Authorization in GitHub repository saleor/saleor prior to 3.1.2.

Missing Authorization

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

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.

        def _resolve_orders(orders):
            requester = get_user_or_app_from_context(info.context)
            if not requester.has_perm(OrderPermissions.MANAGE_ORDERS):
                orders = list(
                    filter(lambda order: order.status != OrderStatus.DRAFT, orders)
                )

            return create_connection_slice(
                orders, info, kwargs, OrderCountableConnection
            )

        return OrdersByUserLoader(info.context).load(root.id).then(_resolve_orders)

        def _resolve_orders(orders):
            requester = get_user_or_app_from_context(info.context)
            if not requester.has_perm(OrderPermissions.MANAGE_ORDERS):
                # allow fetch requestor orders (except drafts)
                if root == info.context.user:
                    orders = list(
                        filter(lambda order: order.status != OrderStatus.DRAFT, orders)
                    )
                else:
                    raise PermissionDenied()

            return create_connection_slice(
                orders, info, kwargs, OrderCountableConnection
            )

        return OrdersByUserLoader(info.context).load(root.id).then(_resolve_orders)

--- a/saleor/graphql/account/types.py
+++ b/saleor/graphql/account/types.py
@@ -1,9 +1,13 @@
         def _resolve_orders(orders):
             requester = get_user_or_app_from_context(info.context)
             if not requester.has_perm(OrderPermissions.MANAGE_ORDERS):
-                orders = list(
-                    filter(lambda order: order.status != OrderStatus.DRAFT, orders)
-                )
+                # allow fetch requestor orders (except drafts)
+                if root == info.context.user:
+                    orders = list(
+                        filter(lambda order: order.status != OrderStatus.DRAFT, orders)
+                    )
+                else:
+                    raise PermissionDenied()

             return create_connection_slice(
                 orders, info, kwargs, OrderCountableConnection


The multifilesystem storage backend in Radicale before 1.1 allows remote attackers to read or write to arbitrary files via a crafted component name.

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.

    def write(self):
        self._create_dirs()
        for component in self.components:
            text = ical.serialize(
                self.tag, self.headers, [component] + self.timezones)
            name = (
                component.name if sys.version_info[0] >= 3 else
                component.name.encode(filesystem.FILESYSTEM_ENCODING))
            filesystem_path = os.path.join(self._filesystem_path, name)
            with filesystem.open(filesystem_path, "w") as fd:
                fd.write(text)

    def remove(self, name):
        filesystem_path = os.path.join(self._filesystem_path, name)
        if os.path.exists(filesystem_path):
            os.remove(filesystem_path)

    def write(self):
        self._create_dirs()
        for component in self.components:
            text = ical.serialize(
                self.tag, self.headers, [component] + self.timezones)
            name = (
                component.name if sys.version_info[0] >= 3 else
                component.name.encode(filesystem.FILESYSTEM_ENCODING))
            if not pathutils.is_safe_filesystem_path_component(name):
                log.LOGGER.debug(
                    "Can't tranlate name safely to filesystem, "
                    "skipping component: %s", name)
                continue
            filesystem_path = os.path.join(self._filesystem_path, name)
            with filesystem.open(filesystem_path, "w") as fd:
                fd.write(text)

    def remove(self, name):
        if not pathutils.is_safe_filesystem_path_component(name):
            log.LOGGER.debug(
                "Can't tranlate name safely to filesystem, "
                "skipping component: %s", name)
            return
        filesystem_path = os.path.join(self._filesystem_path, name)
        if os.path.exists(filesystem_path):
            os.remove(filesystem_path)

--- a/radicale/storage/multifilesystem.py
+++ b/radicale/storage/multifilesystem.py
@@ -6,6 +6,11 @@
             name = (
                 component.name if sys.version_info[0] >= 3 else
                 component.name.encode(filesystem.FILESYSTEM_ENCODING))
+            if not pathutils.is_safe_filesystem_path_component(name):
+                log.LOGGER.debug(
+                    "Can't tranlate name safely to filesystem, "
+                    "skipping component: %s", name)
+                continue
             filesystem_path = os.path.join(self._filesystem_path, name)
             with filesystem.open(filesystem_path, "w") as fd:
                 fd.write(text)

--- a/radicale/storage/multifilesystem.py
+++ b/radicale/storage/multifilesystem.py
@@ -1,4 +1,9 @@
     def remove(self, name):
+        if not pathutils.is_safe_filesystem_path_component(name):
+            log.LOGGER.debug(
+                "Can't tranlate name safely to filesystem, "
+                "skipping component: %s", name)
+            return
         filesystem_path = os.path.join(self._filesystem_path, name)
         if os.path.exists(filesystem_path):
             os.remove(filesystem_path)


PySAML2 before 5.0.0 does not check that the signature in a SAML document is enveloped and thus signature wrapping is effective, i.e., it is affected by XML Signature Wrapping (XSW). The signature information and the node/object that is signed can be in different places and thus the signature verification will succeed, but the wrong data will be used. This specifically affects the verification of assertion that have been signed.

Improper Verification of Cryptographic Signature

The product does not verify, or incorrectly verifies, the cryptographic signature for data.

    def _check_signature(self, decoded_xml, item, node_name=NODE_NAME, origdoc=None, id_attr='', must=False, only_valid_cert=False, issuer=None):
        try:
            _issuer = item.issuer.text.strip()
        except AttributeError:
            _issuer = None

        if _issuer is None:
            try:
                _issuer = issuer.text.strip()
            except AttributeError:
                _issuer = None

        # More trust in certs from metadata then certs in the XML document
        if self.metadata:
            try:
                _certs = self.metadata.certs(_issuer, 'any', 'signing')
            except KeyError:
                _certs = []
            certs = []

            for cert in _certs:
                if isinstance(cert, six.string_types):
                    content = pem_format(cert)
                    tmp = make_temp(content,
                                    suffix=".pem",
                                    decode=False,
                                    delete_tmpfiles=self.delete_tmpfiles)
                    certs.append(tmp)
                else:
                    certs.append(cert)
        else:
            certs = []

        if not certs and not self.only_use_keys_in_metadata:
            logger.debug('==== Certs from instance ====')
            certs = [
                make_temp(content=pem_format(cert),
                          suffix=".pem",
                          decode=False,
                          delete_tmpfiles=self.delete_tmpfiles)
                for cert in cert_from_instance(item)
            ]
        else:
            logger.debug('==== Certs from metadata ==== %s: %s ====', _issuer, certs)

        if not certs:
            raise MissingKey(_issuer)

        verified = False
        last_pem_file = None

        for pem_fd in certs:
            try:
                last_pem_file = pem_fd.name
                if self.verify_signature(
                        decoded_xml,
                        pem_fd.name,
                        node_name=node_name,
                        node_id=item.id,
                        id_attr=id_attr):
                    verified = True
                    break
            except XmlsecError as exc:
                logger.error('check_sig: %s', exc)
                pass
            except Exception as exc:
                logger.error('check_sig: %s', exc)
                raise

        if verified or only_valid_cert:
            if not self.cert_handler.verify_cert(last_pem_file):
                raise CertificateError('Invalid certificate!')
        else:
            raise SignatureError('Failed to verify signature')

        return item

    def _check_signature(self, decoded_xml, item, node_name=NODE_NAME, origdoc=None, id_attr='', must=False, only_valid_cert=False, issuer=None):
        try:
            _issuer = item.issuer.text.strip()
        except AttributeError:
            _issuer = None

        if _issuer is None:
            try:
                _issuer = issuer.text.strip()
            except AttributeError:
                _issuer = None

        # More trust in certs from metadata then certs in the XML document
        if self.metadata:
            try:
                _certs = self.metadata.certs(_issuer, 'any', 'signing')
            except KeyError:
                _certs = []
            certs = []

            for cert in _certs:
                if isinstance(cert, six.string_types):
                    content = pem_format(cert)
                    tmp = make_temp(content,
                                    suffix=".pem",
                                    decode=False,
                                    delete_tmpfiles=self.delete_tmpfiles)
                    certs.append(tmp)
                else:
                    certs.append(cert)
        else:
            certs = []

        if not certs and not self.only_use_keys_in_metadata:
            logger.debug('==== Certs from instance ====')
            certs = [
                make_temp(content=pem_format(cert),
                          suffix=".pem",
                          decode=False,
                          delete_tmpfiles=self.delete_tmpfiles)
                for cert in cert_from_instance(item)
            ]
        else:
            logger.debug('==== Certs from metadata ==== %s: %s ====', _issuer, certs)

        if not certs:
            raise MissingKey(_issuer)

        # saml-core section "5.4 XML Signature Profile" defines constrains on the
        # xmldsig-core facilities. It explicitly dictates that enveloped signatures
        # are the only signatures allowed. This mean that:
        # * Assertion/RequestType/ResponseType elements must have an ID attribute
        # * signatures must have a single Reference element
        # * the Reference element must have a URI attribute
        # * the URI attribute contains an anchor
        # * the anchor points to the enclosing element's ID attribute
        references = item.signature.signed_info.reference
        signatures_must_have_a_single_reference_element = len(references) == 1
        the_Reference_element_must_have_a_URI_attribute = (
            signatures_must_have_a_single_reference_element
            and hasattr(references[0], "uri")
        )
        the_URI_attribute_contains_an_anchor = (
            the_Reference_element_must_have_a_URI_attribute
            and references[0].uri.startswith("#")
            and len(references[0].uri) > 1
        )
        the_anchor_points_to_the_enclosing_element_ID_attribute = (
            the_URI_attribute_contains_an_anchor
            and references[0].uri == "#{id}".format(id=item.id)
        )
        validators = {
            "signatures must have a single reference element": (
                signatures_must_have_a_single_reference_element
            ),
            "the Reference element must have a URI attribute": (
                the_Reference_element_must_have_a_URI_attribute
            ),
            "the URI attribute contains an anchor": (
                the_URI_attribute_contains_an_anchor
            ),
            "the anchor points to the enclosing element ID attribute": (
                the_anchor_points_to_the_enclosing_element_ID_attribute
            ),
        }
        if not all(validators.values()):
            error_context = {
                "message": "Signature failed to meet constraints on xmldsig",
                "validators": validators,
                "item ID": item.id,
                "reference URI": item.signature.signed_info.reference[0].uri,
                "issuer": _issuer,
                "node name": node_name,
                "xml document": decoded_xml,
            }
            raise SignatureError(error_context)

        verified = False
        last_pem_file = None

        for pem_fd in certs:
            try:
                last_pem_file = pem_fd.name
                if self.verify_signature(
                        decoded_xml,
                        pem_fd.name,
                        node_name=node_name,
                        node_id=item.id,
                        id_attr=id_attr):
                    verified = True
                    break
            except XmlsecError as exc:
                logger.error('check_sig: %s', exc)
                pass
            except Exception as exc:
                logger.error('check_sig: %s', exc)
                raise

        if verified or only_valid_cert:
            if not self.cert_handler.verify_cert(last_pem_file):
                raise CertificateError('Invalid certificate!')
        else:
            raise SignatureError('Failed to verify signature')

        return item

--- a/src/saml2/sigver.py
+++ b/src/saml2/sigver.py
@@ -46,6 +46,55 @@
         if not certs:
             raise MissingKey(_issuer)

+        # saml-core section "5.4 XML Signature Profile" defines constrains on the
+        # xmldsig-core facilities. It explicitly dictates that enveloped signatures
+        # are the only signatures allowed. This mean that:
+        # * Assertion/RequestType/ResponseType elements must have an ID attribute
+        # * signatures must have a single Reference element
+        # * the Reference element must have a URI attribute
+        # * the URI attribute contains an anchor
+        # * the anchor points to the enclosing element's ID attribute
+        references = item.signature.signed_info.reference
+        signatures_must_have_a_single_reference_element = len(references) == 1
+        the_Reference_element_must_have_a_URI_attribute = (
+            signatures_must_have_a_single_reference_element
+            and hasattr(references[0], "uri")
+        )
+        the_URI_attribute_contains_an_anchor = (
+            the_Reference_element_must_have_a_URI_attribute
+            and references[0].uri.startswith("#")
+            and len(references[0].uri) > 1
+        )
+        the_anchor_points_to_the_enclosing_element_ID_attribute = (
+            the_URI_attribute_contains_an_anchor
+            and references[0].uri == "#{id}".format(id=item.id)
+        )
+        validators = {
+            "signatures must have a single reference element": (
+                signatures_must_have_a_single_reference_element
+            ),
+            "the Reference element must have a URI attribute": (
+                the_Reference_element_must_have_a_URI_attribute
+            ),
+            "the URI attribute contains an anchor": (
+                the_URI_attribute_contains_an_anchor
+            ),
+            "the anchor points to the enclosing element ID attribute": (
+                the_anchor_points_to_the_enclosing_element_ID_attribute
+            ),
+        }
+        if not all(validators.values()):
+            error_context = {
+                "message": "Signature failed to meet constraints on xmldsig",
+                "validators": validators,
+                "item ID": item.id,
+                "reference URI": item.signature.signed_info.reference[0].uri,
+                "issuer": _issuer,
+                "node name": node_name,
+                "xml document": decoded_xml,
+            }
+            raise SignatureError(error_context)
+
         verified = False
         last_pem_file = None


Mongoose before 8.9.5 can improperly use a nested $where filter with a populate() match, leading to search injection. NOTE: this issue exists because of an incomplete fix for CVE-2024-53900.

module.exports = function getModelsMapForPopulate(model, docs, options) {
  let doc;
  const len = docs.length;
  const map = [];
  const modelNameFromQuery = options.model && options.model.modelName || options.model;
  let schema;
  let refPath;
  let modelNames;
  const available = {};

  const modelSchema = model.schema;

  // Populating a nested path should always be a no-op re: #9073.
  // People shouldn't do this, but apparently they do.
  if (options._localModel != null && options._localModel.schema.nested[options.path]) {
    return [];
  }

  const _virtualRes = getVirtual(model.schema, options.path);
  const virtual = _virtualRes == null ? null : _virtualRes.virtual;
  if (virtual != null) {
    return _virtualPopulate(model, docs, options, _virtualRes);
  }

  let allSchemaTypes = getSchemaTypes(model, modelSchema, null, options.path);
  allSchemaTypes = Array.isArray(allSchemaTypes) ? allSchemaTypes : [allSchemaTypes].filter(v => v != null);

  if (allSchemaTypes.length === 0 && options.strictPopulate !== false && options._localModel != null) {
    return new StrictPopulate(options._fullPath || options.path);
  }

  for (let i = 0; i < len; i++) {
    doc = docs[i];
    let justOne = null;

    const docSchema = doc != null && doc.$__ != null ? doc.$__schema : modelSchema;
    schema = getSchemaTypes(model, docSchema, doc, options.path);

    // Special case: populating a path that's a DocumentArray unless
    // there's an explicit `ref` or `refPath` re: gh-8946
    if (schema != null &&
        schema.$isMongooseDocumentArray &&
        schema.options.ref == null &&
        schema.options.refPath == null) {
      continue;
    }
    const isUnderneathDocArray = schema && schema.$isUnderneathDocArray;
    if (isUnderneathDocArray && get(options, 'options.sort') != null) {
      return new MongooseError('Cannot populate with `sort` on path ' + options.path +
        ' because it is a subproperty of a document array');
    }

    modelNames = null;
    let isRefPath = false;
    let normalizedRefPath = null;
    let schemaOptions = null;
    let modelNamesInOrder = null;

    if (schema != null && schema.instance === 'Embedded') {
      if (schema.options.ref) {
        const data = {
          localField: options.path + '._id',
          foreignField: '_id',
          justOne: true
        };
        const res = _getModelNames(doc, schema, modelNameFromQuery, model);

        const unpopulatedValue = mpath.get(options.path, doc);
        const id = mpath.get('_id', unpopulatedValue);
        addModelNamesToMap(model, map, available, res.modelNames, options, data, id, doc, schemaOptions, unpopulatedValue);
      }
      // No-op if no `ref` set. See gh-11538
      continue;
    }

    if (Array.isArray(schema)) {
      const schemasArray = schema;
      for (const _schema of schemasArray) {
        let _modelNames;
        let res;
        try {
          res = _getModelNames(doc, _schema, modelNameFromQuery, model);
          _modelNames = res.modelNames;
          isRefPath = isRefPath || res.isRefPath;
          normalizedRefPath = normalizedRefPath || res.refPath;
          justOne = res.justOne;
        } catch (error) {
          return error;
        }

        if (isRefPath && !res.isRefPath) {
          continue;
        }
        if (!_modelNames) {
          continue;
        }
        modelNames = modelNames || [];
        for (const modelName of _modelNames) {
          if (modelNames.indexOf(modelName) === -1) {
            modelNames.push(modelName);
          }
        }
      }
    } else {
      try {
        const res = _getModelNames(doc, schema, modelNameFromQuery, model);
        modelNames = res.modelNames;
        isRefPath = res.isRefPath;
        normalizedRefPath = normalizedRefPath || res.refPath;
        justOne = res.justOne;
        schemaOptions = get(schema, 'options.populate', null);
        // Dedupe, because `refPath` can return duplicates of the same model name,
        // and that causes perf issues.
        if (isRefPath) {
          modelNamesInOrder = modelNames;
          modelNames = Array.from(new Set(modelNames));
        }
      } catch (error) {
        return error;
      }

      if (!modelNames) {
        continue;
      }
    }

    const data = {};
    const localField = options.path;
    const foreignField = '_id';

    // `justOne = null` means we don't know from the schema whether the end
    // result should be an array or a single doc. This can result from
    // populating a POJO using `Model.populate()`
    if ('justOne' in options && options.justOne !== void 0) {
      justOne = options.justOne;
    } else if (schema && !schema[schemaMixedSymbol]) {
      // Skip Mixed types because we explicitly don't do casting on those.
      if (options.path.endsWith('.' + schema.path) || options.path === schema.path) {
        justOne = Array.isArray(schema) ?
          schema.every(schema => !schema.$isMongooseArray) :
          !schema.$isMongooseArray;
      }
    }

    if (!modelNames) {
      continue;
    }

    data.isVirtual = false;
    data.justOne = justOne;
    data.localField = localField;
    data.foreignField = foreignField;

    // Get local fields
    const ret = _getLocalFieldValues(doc, localField, model, options, null, schema);

    const id = String(utils.getValue(foreignField, doc));
    options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;

    let match = get(options, 'match', null);

    const hasMatchFunction = typeof match === 'function';
    if (hasMatchFunction) {
      match = match.call(doc, doc);
    }
    if (Array.isArray(match)) {
      for (const item of match) {
        if (item != null && item.$where) {
          throw new MongooseError('Cannot use $where filter with populate() match');
        }
      }
    } else if (match != null && match.$where != null) {
      throw new MongooseError('Cannot use $where filter with populate() match');
    }
    data.match = match;
    data.hasMatchFunction = hasMatchFunction;
    data.isRefPath = isRefPath;
    data.modelNamesInOrder = modelNamesInOrder;

    if (isRefPath) {
      const embeddedDiscriminatorModelNames = _findRefPathForDiscriminators(doc,
        modelSchema, data, options, normalizedRefPath, ret);

      modelNames = embeddedDiscriminatorModelNames || modelNames;
    }

    try {
      addModelNamesToMap(model, map, available, modelNames, options, data, ret, doc, schemaOptions);
    } catch (err) {
      return err;
    }
  }
  return map;

  function _getModelNames(doc, schema, modelNameFromQuery, model) {
    let modelNames;
    let isRefPath = false;
    let justOne = null;

    const originalSchema = schema;
    if (schema && schema.instance === 'Array') {
      schema = schema.caster;
    }
    if (schema && schema.$isSchemaMap) {
      schema = schema.$__schemaType;
    }

    const ref = schema && schema.options && schema.options.ref;
    refPath = schema && schema.options && schema.options.refPath;
    if (schema != null &&
        schema[schemaMixedSymbol] &&
        !ref &&
        !refPath &&
        !modelNameFromQuery) {
      return { modelNames: null };
    }

    if (modelNameFromQuery) {
      modelNames = [modelNameFromQuery]; // query options
    } else if (refPath != null) {
      if (typeof refPath === 'function') {
        const subdocPath = options.path.slice(0, options.path.length - schema.path.length - 1);
        const vals = mpath.get(subdocPath, doc, lookupLocalFields);
        const subdocsBeingPopulated = Array.isArray(vals) ?
          utils.array.flatten(vals) :
          (vals ? [vals] : []);

        modelNames = new Set();
        for (const subdoc of subdocsBeingPopulated) {
          refPath = refPath.call(subdoc, subdoc, options.path);
          modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection).
            forEach(name => modelNames.add(name));
        }
        modelNames = Array.from(modelNames);
      } else {
        modelNames = modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection);
      }

      isRefPath = true;
    } else {
      let ref;
      let refPath;
      let schemaForCurrentDoc;
      let discriminatorValue;
      let modelForCurrentDoc = model;
      const discriminatorKey = model.schema.options.discriminatorKey;

      if (!schema && discriminatorKey && (discriminatorValue = utils.getValue(discriminatorKey, doc))) {
        // `modelNameForFind` is the discriminator value, so we might need
        // find the discriminated model name
        const discriminatorModel = getDiscriminatorByValue(model.discriminators, discriminatorValue) || model;
        if (discriminatorModel != null) {
          modelForCurrentDoc = discriminatorModel;
        } else {
          try {
            modelForCurrentDoc = _getModelFromConn(model.db, discriminatorValue);
          } catch (error) {
            return error;
          }
        }

        schemaForCurrentDoc = modelForCurrentDoc.schema._getSchema(options.path);

        if (schemaForCurrentDoc && schemaForCurrentDoc.caster) {
          schemaForCurrentDoc = schemaForCurrentDoc.caster;
        }
      } else {
        schemaForCurrentDoc = schema;
      }

      if (originalSchema && originalSchema.path.endsWith('.$*')) {
        justOne = !originalSchema.$isMongooseArray && !originalSchema._arrayPath;
      } else if (schemaForCurrentDoc != null) {
        justOne = !schemaForCurrentDoc.$isMongooseArray && !schemaForCurrentDoc._arrayPath;
      }

      if ((ref = get(schemaForCurrentDoc, 'options.ref')) != null) {
        if (schemaForCurrentDoc != null &&
            typeof ref === 'function' &&
            options.path.endsWith('.' + schemaForCurrentDoc.path)) {
          // Ensure correct context for ref functions: subdoc, not top-level doc. See gh-8469
          modelNames = new Set();

          const subdocPath = options.path.slice(0, options.path.length - schemaForCurrentDoc.path.length - 1);
          const vals = mpath.get(subdocPath, doc, lookupLocalFields);
          const subdocsBeingPopulated = Array.isArray(vals) ?
            utils.array.flatten(vals) :
            (vals ? [vals] : []);
          for (const subdoc of subdocsBeingPopulated) {
            modelNames.add(handleRefFunction(ref, subdoc));
          }

          if (subdocsBeingPopulated.length === 0) {
            modelNames = [handleRefFunction(ref, doc)];
          } else {
            modelNames = Array.from(modelNames);
          }
        } else {
          ref = handleRefFunction(ref, doc);
          modelNames = [ref];
        }
      } else if ((schemaForCurrentDoc = get(schema, 'options.refPath')) != null) {
        isRefPath = true;
        if (typeof refPath === 'function') {
          const subdocPath = options.path.slice(0, options.path.length - schemaForCurrentDoc.path.length - 1);
          const vals = mpath.get(subdocPath, doc, lookupLocalFields);
          const subdocsBeingPopulated = Array.isArray(vals) ?
            utils.array.flatten(vals) :
            (vals ? [vals] : []);

          modelNames = new Set();
          for (const subdoc of subdocsBeingPopulated) {
            refPath = refPath.call(subdoc, subdoc, options.path);
            modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection).
              forEach(name => modelNames.add(name));
          }
          modelNames = Array.from(modelNames);
        } else {
          modelNames = modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection);
        }
      }
    }

    if (!modelNames) {
      // `Model.populate()` on a POJO with no known local model. Default to using the `Model`
      if (options._localModel == null) {
        modelNames = [model.modelName];
      } else {
        return { modelNames: modelNames, justOne: justOne, isRefPath: isRefPath, refPath: refPath };
      }
    }

    if (!Array.isArray(modelNames)) {
      modelNames = [modelNames];
    }

    return { modelNames: modelNames, justOne: justOne, isRefPath: isRefPath, refPath: refPath };
  }
};

function _virtualPopulate(model, docs, options, _virtualRes) {
  const map = [];
  const available = {};
  const virtual = _virtualRes.virtual;

  for (const doc of docs) {
    let modelNames = null;
    const data = {};

    // localField and foreignField
    let localField;
    const virtualPrefix = _virtualRes.nestedSchemaPath ?
      _virtualRes.nestedSchemaPath + '.' : '';
    if (typeof options.localField === 'string') {
      localField = options.localField;
    } else if (typeof virtual.options.localField === 'function') {
      localField = virtualPrefix + virtual.options.localField.call(doc, doc);
    } else if (Array.isArray(virtual.options.localField)) {
      localField = virtual.options.localField.map(field => virtualPrefix + field);
    } else {
      localField = virtualPrefix + virtual.options.localField;
    }
    data.count = virtual.options.count;

    if (virtual.options.skip != null && !options.hasOwnProperty('skip')) {
      options.skip = virtual.options.skip;
    }
    if (virtual.options.limit != null && !options.hasOwnProperty('limit')) {
      options.limit = virtual.options.limit;
    }
    if (virtual.options.perDocumentLimit != null && !options.hasOwnProperty('perDocumentLimit')) {
      options.perDocumentLimit = virtual.options.perDocumentLimit;
    }
    let foreignField = virtual.options.foreignField;

    if (!localField || !foreignField) {
      return new MongooseError('If you are populating a virtual, you must set the ' +
        'localField and foreignField options');
    }

    if (typeof localField === 'function') {
      localField = localField.call(doc, doc);
    }
    if (typeof foreignField === 'function') {
      foreignField = foreignField.call(doc, doc);
    }

    data.isRefPath = false;

    // `justOne = null` means we don't know from the schema whether the end
    // result should be an array or a single doc. This can result from
    // populating a POJO using `Model.populate()`
    let justOne = null;
    if ('justOne' in options && options.justOne !== void 0) {
      justOne = options.justOne;
    }

    if (virtual.options.refPath) {
      modelNames =
        modelNamesFromRefPath(virtual.options.refPath, doc, options.path);
      justOne = !!virtual.options.justOne;
      data.isRefPath = true;
    } else if (virtual.options.ref) {
      let normalizedRef;
      if (typeof virtual.options.ref === 'function' && !virtual.options.ref[modelSymbol]) {
        normalizedRef = virtual.options.ref.call(doc, doc);
      } else {
        normalizedRef = virtual.options.ref;
      }
      justOne = !!virtual.options.justOne;
      // When referencing nested arrays, the ref should be an Array
      // of modelNames.
      if (Array.isArray(normalizedRef)) {
        modelNames = normalizedRef;
      } else {
        modelNames = [normalizedRef];
      }
    }

    data.isVirtual = true;
    data.virtual = virtual;
    data.justOne = justOne;

    // `match`
    let match = get(options, 'match', null) ||
      get(data, 'virtual.options.match', null) ||
      get(data, 'virtual.options.options.match', null);

    let hasMatchFunction = typeof match === 'function';
    if (hasMatchFunction) {
      match = match.call(doc, doc);
    }

    if (Array.isArray(localField) && Array.isArray(foreignField) && localField.length === foreignField.length) {
      match = Object.assign({}, match);
      for (let i = 1; i < localField.length; ++i) {
        match[foreignField[i]] = convertTo_id(mpath.get(localField[i], doc, lookupLocalFields), model.schema);
        hasMatchFunction = true;
      }

      localField = localField[0];
      foreignField = foreignField[0];
    }
    data.localField = localField;
    data.foreignField = foreignField;
    data.match = match;
    data.hasMatchFunction = hasMatchFunction;

    if (Array.isArray(match)) {
      for (const item of match) {
        if (item != null && item.$where) {
          throw new MongooseError('Cannot use $where filter with populate() match');
        }
      }
    } else if (match != null && match.$where != null) {
      throw new MongooseError('Cannot use $where filter with populate() match');
    }

    // Get local fields
    const ret = _getLocalFieldValues(doc, localField, model, options, virtual);

    try {
      addModelNamesToMap(model, map, available, modelNames, options, data, ret, doc);
    } catch (err) {
      return err;
    }
  }

  return map;
}

module.exports = function getModelsMapForPopulate(model, docs, options) {
  let doc;
  const len = docs.length;
  const map = [];
  const modelNameFromQuery = options.model && options.model.modelName || options.model;
  let schema;
  let refPath;
  let modelNames;
  const available = {};

  const modelSchema = model.schema;

  // Populating a nested path should always be a no-op re: #9073.
  // People shouldn't do this, but apparently they do.
  if (options._localModel != null && options._localModel.schema.nested[options.path]) {
    return [];
  }

  const _virtualRes = getVirtual(model.schema, options.path);
  const virtual = _virtualRes == null ? null : _virtualRes.virtual;
  if (virtual != null) {
    return _virtualPopulate(model, docs, options, _virtualRes);
  }

  let allSchemaTypes = getSchemaTypes(model, modelSchema, null, options.path);
  allSchemaTypes = Array.isArray(allSchemaTypes) ? allSchemaTypes : [allSchemaTypes].filter(v => v != null);

  if (allSchemaTypes.length === 0 && options.strictPopulate !== false && options._localModel != null) {
    return new StrictPopulate(options._fullPath || options.path);
  }

  for (let i = 0; i < len; i++) {
    doc = docs[i];
    let justOne = null;

    const docSchema = doc != null && doc.$__ != null ? doc.$__schema : modelSchema;
    schema = getSchemaTypes(model, docSchema, doc, options.path);

    // Special case: populating a path that's a DocumentArray unless
    // there's an explicit `ref` or `refPath` re: gh-8946
    if (schema != null &&
        schema.$isMongooseDocumentArray &&
        schema.options.ref == null &&
        schema.options.refPath == null) {
      continue;
    }
    const isUnderneathDocArray = schema && schema.$isUnderneathDocArray;
    if (isUnderneathDocArray && get(options, 'options.sort') != null) {
      return new MongooseError('Cannot populate with `sort` on path ' + options.path +
        ' because it is a subproperty of a document array');
    }

    modelNames = null;
    let isRefPath = false;
    let normalizedRefPath = null;
    let schemaOptions = null;
    let modelNamesInOrder = null;

    if (schema != null && schema.instance === 'Embedded') {
      if (schema.options.ref) {
        const data = {
          localField: options.path + '._id',
          foreignField: '_id',
          justOne: true
        };
        const res = _getModelNames(doc, schema, modelNameFromQuery, model);

        const unpopulatedValue = mpath.get(options.path, doc);
        const id = mpath.get('_id', unpopulatedValue);
        addModelNamesToMap(model, map, available, res.modelNames, options, data, id, doc, schemaOptions, unpopulatedValue);
      }
      // No-op if no `ref` set. See gh-11538
      continue;
    }

    if (Array.isArray(schema)) {
      const schemasArray = schema;
      for (const _schema of schemasArray) {
        let _modelNames;
        let res;
        try {
          res = _getModelNames(doc, _schema, modelNameFromQuery, model);
          _modelNames = res.modelNames;
          isRefPath = isRefPath || res.isRefPath;
          normalizedRefPath = normalizedRefPath || res.refPath;
          justOne = res.justOne;
        } catch (error) {
          return error;
        }

        if (isRefPath && !res.isRefPath) {
          continue;
        }
        if (!_modelNames) {
          continue;
        }
        modelNames = modelNames || [];
        for (const modelName of _modelNames) {
          if (modelNames.indexOf(modelName) === -1) {
            modelNames.push(modelName);
          }
        }
      }
    } else {
      try {
        const res = _getModelNames(doc, schema, modelNameFromQuery, model);
        modelNames = res.modelNames;
        isRefPath = res.isRefPath;
        normalizedRefPath = normalizedRefPath || res.refPath;
        justOne = res.justOne;
        schemaOptions = get(schema, 'options.populate', null);
        // Dedupe, because `refPath` can return duplicates of the same model name,
        // and that causes perf issues.
        if (isRefPath) {
          modelNamesInOrder = modelNames;
          modelNames = Array.from(new Set(modelNames));
        }
      } catch (error) {
        return error;
      }

      if (!modelNames) {
        continue;
      }
    }

    const data = {};
    const localField = options.path;
    const foreignField = '_id';

    // `justOne = null` means we don't know from the schema whether the end
    // result should be an array or a single doc. This can result from
    // populating a POJO using `Model.populate()`
    if ('justOne' in options && options.justOne !== void 0) {
      justOne = options.justOne;
    } else if (schema && !schema[schemaMixedSymbol]) {
      // Skip Mixed types because we explicitly don't do casting on those.
      if (options.path.endsWith('.' + schema.path) || options.path === schema.path) {
        justOne = Array.isArray(schema) ?
          schema.every(schema => !schema.$isMongooseArray) :
          !schema.$isMongooseArray;
      }
    }

    if (!modelNames) {
      continue;
    }

    data.isVirtual = false;
    data.justOne = justOne;
    data.localField = localField;
    data.foreignField = foreignField;

    // Get local fields
    const ret = _getLocalFieldValues(doc, localField, model, options, null, schema);

    const id = String(utils.getValue(foreignField, doc));
    options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;

    let match = get(options, 'match', null);

    const hasMatchFunction = typeof match === 'function';
    if (hasMatchFunction) {
      match = match.call(doc, doc);
    }
    throwOn$where(match);
    data.match = match;
    data.hasMatchFunction = hasMatchFunction;
    data.isRefPath = isRefPath;
    data.modelNamesInOrder = modelNamesInOrder;

    if (isRefPath) {
      const embeddedDiscriminatorModelNames = _findRefPathForDiscriminators(doc,
        modelSchema, data, options, normalizedRefPath, ret);

      modelNames = embeddedDiscriminatorModelNames || modelNames;
    }

    try {
      addModelNamesToMap(model, map, available, modelNames, options, data, ret, doc, schemaOptions);
    } catch (err) {
      return err;
    }
  }
  return map;

  function _getModelNames(doc, schema, modelNameFromQuery, model) {
    let modelNames;
    let isRefPath = false;
    let justOne = null;

    const originalSchema = schema;
    if (schema && schema.instance === 'Array') {
      schema = schema.caster;
    }
    if (schema && schema.$isSchemaMap) {
      schema = schema.$__schemaType;
    }

    const ref = schema && schema.options && schema.options.ref;
    refPath = schema && schema.options && schema.options.refPath;
    if (schema != null &&
        schema[schemaMixedSymbol] &&
        !ref &&
        !refPath &&
        !modelNameFromQuery) {
      return { modelNames: null };
    }

    if (modelNameFromQuery) {
      modelNames = [modelNameFromQuery]; // query options
    } else if (refPath != null) {
      if (typeof refPath === 'function') {
        const subdocPath = options.path.slice(0, options.path.length - schema.path.length - 1);
        const vals = mpath.get(subdocPath, doc, lookupLocalFields);
        const subdocsBeingPopulated = Array.isArray(vals) ?
          utils.array.flatten(vals) :
          (vals ? [vals] : []);

        modelNames = new Set();
        for (const subdoc of subdocsBeingPopulated) {
          refPath = refPath.call(subdoc, subdoc, options.path);
          modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection).
            forEach(name => modelNames.add(name));
        }
        modelNames = Array.from(modelNames);
      } else {
        modelNames = modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection);
      }

      isRefPath = true;
    } else {
      let ref;
      let refPath;
      let schemaForCurrentDoc;
      let discriminatorValue;
      let modelForCurrentDoc = model;
      const discriminatorKey = model.schema.options.discriminatorKey;

      if (!schema && discriminatorKey && (discriminatorValue = utils.getValue(discriminatorKey, doc))) {
        // `modelNameForFind` is the discriminator value, so we might need
        // find the discriminated model name
        const discriminatorModel = getDiscriminatorByValue(model.discriminators, discriminatorValue) || model;
        if (discriminatorModel != null) {
          modelForCurrentDoc = discriminatorModel;
        } else {
          try {
            modelForCurrentDoc = _getModelFromConn(model.db, discriminatorValue);
          } catch (error) {
            return error;
          }
        }

        schemaForCurrentDoc = modelForCurrentDoc.schema._getSchema(options.path);

        if (schemaForCurrentDoc && schemaForCurrentDoc.caster) {
          schemaForCurrentDoc = schemaForCurrentDoc.caster;
        }
      } else {
        schemaForCurrentDoc = schema;
      }

      if (originalSchema && originalSchema.path.endsWith('.$*')) {
        justOne = !originalSchema.$isMongooseArray && !originalSchema._arrayPath;
      } else if (schemaForCurrentDoc != null) {
        justOne = !schemaForCurrentDoc.$isMongooseArray && !schemaForCurrentDoc._arrayPath;
      }

      if ((ref = get(schemaForCurrentDoc, 'options.ref')) != null) {
        if (schemaForCurrentDoc != null &&
            typeof ref === 'function' &&
            options.path.endsWith('.' + schemaForCurrentDoc.path)) {
          // Ensure correct context for ref functions: subdoc, not top-level doc. See gh-8469
          modelNames = new Set();

          const subdocPath = options.path.slice(0, options.path.length - schemaForCurrentDoc.path.length - 1);
          const vals = mpath.get(subdocPath, doc, lookupLocalFields);
          const subdocsBeingPopulated = Array.isArray(vals) ?
            utils.array.flatten(vals) :
            (vals ? [vals] : []);
          for (const subdoc of subdocsBeingPopulated) {
            modelNames.add(handleRefFunction(ref, subdoc));
          }

          if (subdocsBeingPopulated.length === 0) {
            modelNames = [handleRefFunction(ref, doc)];
          } else {
            modelNames = Array.from(modelNames);
          }
        } else {
          ref = handleRefFunction(ref, doc);
          modelNames = [ref];
        }
      } else if ((schemaForCurrentDoc = get(schema, 'options.refPath')) != null) {
        isRefPath = true;
        if (typeof refPath === 'function') {
          const subdocPath = options.path.slice(0, options.path.length - schemaForCurrentDoc.path.length - 1);
          const vals = mpath.get(subdocPath, doc, lookupLocalFields);
          const subdocsBeingPopulated = Array.isArray(vals) ?
            utils.array.flatten(vals) :
            (vals ? [vals] : []);

          modelNames = new Set();
          for (const subdoc of subdocsBeingPopulated) {
            refPath = refPath.call(subdoc, subdoc, options.path);
            modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection).
              forEach(name => modelNames.add(name));
          }
          modelNames = Array.from(modelNames);
        } else {
          modelNames = modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection);
        }
      }
    }

    if (!modelNames) {
      // `Model.populate()` on a POJO with no known local model. Default to using the `Model`
      if (options._localModel == null) {
        modelNames = [model.modelName];
      } else {
        return { modelNames: modelNames, justOne: justOne, isRefPath: isRefPath, refPath: refPath };
      }
    }

    if (!Array.isArray(modelNames)) {
      modelNames = [modelNames];
    }

    return { modelNames: modelNames, justOne: justOne, isRefPath: isRefPath, refPath: refPath };
  }
};

function _virtualPopulate(model, docs, options, _virtualRes) {
  const map = [];
  const available = {};
  const virtual = _virtualRes.virtual;

  for (const doc of docs) {
    let modelNames = null;
    const data = {};

    // localField and foreignField
    let localField;
    const virtualPrefix = _virtualRes.nestedSchemaPath ?
      _virtualRes.nestedSchemaPath + '.' : '';
    if (typeof options.localField === 'string') {
      localField = options.localField;
    } else if (typeof virtual.options.localField === 'function') {
      localField = virtualPrefix + virtual.options.localField.call(doc, doc);
    } else if (Array.isArray(virtual.options.localField)) {
      localField = virtual.options.localField.map(field => virtualPrefix + field);
    } else {
      localField = virtualPrefix + virtual.options.localField;
    }
    data.count = virtual.options.count;

    if (virtual.options.skip != null && !options.hasOwnProperty('skip')) {
      options.skip = virtual.options.skip;
    }
    if (virtual.options.limit != null && !options.hasOwnProperty('limit')) {
      options.limit = virtual.options.limit;
    }
    if (virtual.options.perDocumentLimit != null && !options.hasOwnProperty('perDocumentLimit')) {
      options.perDocumentLimit = virtual.options.perDocumentLimit;
    }
    let foreignField = virtual.options.foreignField;

    if (!localField || !foreignField) {
      return new MongooseError('If you are populating a virtual, you must set the ' +
        'localField and foreignField options');
    }

    if (typeof localField === 'function') {
      localField = localField.call(doc, doc);
    }
    if (typeof foreignField === 'function') {
      foreignField = foreignField.call(doc, doc);
    }

    data.isRefPath = false;

    // `justOne = null` means we don't know from the schema whether the end
    // result should be an array or a single doc. This can result from
    // populating a POJO using `Model.populate()`
    let justOne = null;
    if ('justOne' in options && options.justOne !== void 0) {
      justOne = options.justOne;
    }

    if (virtual.options.refPath) {
      modelNames =
        modelNamesFromRefPath(virtual.options.refPath, doc, options.path);
      justOne = !!virtual.options.justOne;
      data.isRefPath = true;
    } else if (virtual.options.ref) {
      let normalizedRef;
      if (typeof virtual.options.ref === 'function' && !virtual.options.ref[modelSymbol]) {
        normalizedRef = virtual.options.ref.call(doc, doc);
      } else {
        normalizedRef = virtual.options.ref;
      }
      justOne = !!virtual.options.justOne;
      // When referencing nested arrays, the ref should be an Array
      // of modelNames.
      if (Array.isArray(normalizedRef)) {
        modelNames = normalizedRef;
      } else {
        modelNames = [normalizedRef];
      }
    }

    data.isVirtual = true;
    data.virtual = virtual;
    data.justOne = justOne;

    // `match`
    let match = get(options, 'match', null) ||
      get(data, 'virtual.options.match', null) ||
      get(data, 'virtual.options.options.match', null);

    let hasMatchFunction = typeof match === 'function';
    if (hasMatchFunction) {
      match = match.call(doc, doc);
    }

    if (Array.isArray(localField) && Array.isArray(foreignField) && localField.length === foreignField.length) {
      match = Object.assign({}, match);
      for (let i = 1; i < localField.length; ++i) {
        match[foreignField[i]] = convertTo_id(mpath.get(localField[i], doc, lookupLocalFields), model.schema);
        hasMatchFunction = true;
      }

      localField = localField[0];
      foreignField = foreignField[0];
    }
    data.localField = localField;
    data.foreignField = foreignField;
    data.match = match;
    data.hasMatchFunction = hasMatchFunction;

    throwOn$where(match);

    // Get local fields
    const ret = _getLocalFieldValues(doc, localField, model, options, virtual);

    try {
      addModelNamesToMap(model, map, available, modelNames, options, data, ret, doc);
    } catch (err) {
      return err;
    }
  }

  return map;
}

function throwOn$where(match) {
  if (match == null) {
    return;
  }
  if (typeof match !== 'object') {
    return;
  }
  for (const key of Object.keys(match)) {
    if (key === '$where') {
      throw new MongooseError('Cannot use $where filter with populate() match');
    }
    if (match[key] != null && typeof match[key] === 'object') {
      throwOn$where(match[key]);
    }
  }
}

--- a/lib/helpers/populate/getModelsMapForPopulate.js
+++ b/lib/helpers/populate/getModelsMapForPopulate.js
@@ -163,15 +163,7 @@
     if (hasMatchFunction) {
       match = match.call(doc, doc);
     }
-    if (Array.isArray(match)) {
-      for (const item of match) {
-        if (item != null && item.$where) {
-          throw new MongooseError('Cannot use $where filter with populate() match');
-        }
-      }
-    } else if (match != null && match.$where != null) {
-      throw new MongooseError('Cannot use $where filter with populate() match');
-    }
+    throwOn$where(match);
     data.match = match;
     data.hasMatchFunction = hasMatchFunction;
     data.isRefPath = isRefPath;

--- a/lib/helpers/populate/getModelsMapForPopulate.js
+++ b/lib/helpers/populate/getModelsMapForPopulate.js
@@ -106,15 +106,7 @@
     data.match = match;
     data.hasMatchFunction = hasMatchFunction;

-    if (Array.isArray(match)) {
-      for (const item of match) {
-        if (item != null && item.$where) {
-          throw new MongooseError('Cannot use $where filter with populate() match');
-        }
-      }
-    } else if (match != null && match.$where != null) {
-      throw new MongooseError('Cannot use $where filter with populate() match');
-    }
+    throwOn$where(match);

     // Get local fields
     const ret = _getLocalFieldValues(doc, localField, model, options, virtual);

--- /dev/null
+++ b/lib/helpers/populate/getModelsMapForPopulate.js
@@ -0,0 +1,16 @@
+function throwOn$where(match) {
+  if (match == null) {
+    return;
+  }
+  if (typeof match !== 'object') {
+    return;
+  }
+  for (const key of Object.keys(match)) {
+    if (key === '$where') {
+      throw new MongooseError('Cannot use $where filter with populate() match');
+    }
+    if (match[key] != null && typeof match[key] === 'object') {
+      throwOn$where(match[key]);
+    }
+  }
+}


The olmax99/helm-flask-celery repository before 2022-05-25 on GitHub allows absolute path traversal because the Flask send_file function is used unsafely.

def route_frontend(path):
    # ...could be a static file needed by the front end that
    # doesn't use the `static` path (like in `<script src="bundle.js">`)
    file_path = os.path.join(current_app.template_folder, path)
    if os.path.isfile(file_path):
        return send_file(file_path)
    # ...or should be handled by the SPA's "router" in front end
    else:
        index_path = os.path.join(current_app.static_folder, 'index.html')
        return send_file(index_path)

def route_frontend(path):
    # ...could be a static file needed by the front end that
    # doesn't use the `static` path (like in `<script src="bundle.js">`)
    file_path = safe_join(current_app.template_folder, path)
    if os.path.isfile(file_path):
        return send_file(file_path)
    # ...or should be handled by the SPA's "router" in front end
    else:
        index_path = os.path.join(current_app.static_folder, 'index.html')
        return send_file(index_path)

--- a/webapiservice/flaskapi/core/app_setup.py
+++ b/webapiservice/flaskapi/core/app_setup.py
@@ -1,7 +1,7 @@
 def route_frontend(path):
     # ...could be a static file needed by the front end that
     # doesn't use the `static` path (like in `<script src="bundle.js">`)
-    file_path = os.path.join(current_app.template_folder, path)
+    file_path = safe_join(current_app.template_folder, path)
     if os.path.isfile(file_path):
         return send_file(file_path)
     # ...or should be handled by the SPA's "router" in front end


An SSRF (Server-Side Request Forgery) vulnerability exists in the gradio-app/gradio repository, allowing attackers to scan and identify open ports within an internal network. By manipulating the 'file' parameter in a GET request, an attacker can discern the status of internal ports based on the presence of a 'Location' header or a 'File not allowed' error in the response.

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.

        async def file(path_or_url: str, request: fastapi.Request):
            blocks = app.get_blocks()
            if utils.validate_url(path_or_url):
                return RedirectResponse(
                    url=path_or_url, status_code=status.HTTP_302_FOUND
                )
            abs_path = utils.abspath(path_or_url)

            in_blocklist = any(
                utils.is_in_or_equal(abs_path, blocked_path)
                for blocked_path in blocks.blocked_paths
            )
            is_dir = abs_path.is_dir()

            if in_blocklist or is_dir:
                raise HTTPException(403, f"File not allowed: {path_or_url}.")

            created_by_app = str(abs_path) in set().union(*blocks.temp_file_sets)
            in_allowlist = any(
                utils.is_in_or_equal(abs_path, allowed_path)
                for allowed_path in blocks.allowed_paths
            )
            was_uploaded = utils.is_in_or_equal(abs_path, app.uploaded_file_dir)
            is_cached_example = utils.is_in_or_equal(
                abs_path, utils.abspath(CACHED_FOLDER)
            )

            if not (
                created_by_app or in_allowlist or was_uploaded or is_cached_example
            ):
                raise HTTPException(403, f"File not allowed: {path_or_url}.")

            if not abs_path.exists():
                raise HTTPException(404, f"File not found: {path_or_url}.")

            range_val = request.headers.get("Range", "").strip()
            if range_val.startswith("bytes=") and "-" in range_val:
                range_val = range_val[6:]
                start, end = range_val.split("-")
                if start.isnumeric() and end.isnumeric():
                    start = int(start)
                    end = int(end)
                    response = ranged_response.RangedFileResponse(
                        abs_path,
                        ranged_response.OpenRange(start, end),
                        dict(request.headers),
                        stat_result=os.stat(abs_path),
                    )
                    return response

            return FileResponse(abs_path, headers={"Accept-Ranges": "bytes"})

        async def file(path_or_url: str, request: fastapi.Request):
            blocks = app.get_blocks()
            if client_utils.is_http_url_like(path_or_url):
                return RedirectResponse(
                    url=path_or_url, status_code=status.HTTP_302_FOUND
                )
            abs_path = utils.abspath(path_or_url)

            in_blocklist = any(
                utils.is_in_or_equal(abs_path, blocked_path)
                for blocked_path in blocks.blocked_paths
            )
            is_dir = abs_path.is_dir()

            if in_blocklist or is_dir:
                raise HTTPException(403, f"File not allowed: {path_or_url}.")

            created_by_app = str(abs_path) in set().union(*blocks.temp_file_sets)
            in_allowlist = any(
                utils.is_in_or_equal(abs_path, allowed_path)
                for allowed_path in blocks.allowed_paths
            )
            was_uploaded = utils.is_in_or_equal(abs_path, app.uploaded_file_dir)
            is_cached_example = utils.is_in_or_equal(
                abs_path, utils.abspath(CACHED_FOLDER)
            )

            if not (
                created_by_app or in_allowlist or was_uploaded or is_cached_example
            ):
                raise HTTPException(403, f"File not allowed: {path_or_url}.")

            if not abs_path.exists():
                raise HTTPException(404, f"File not found: {path_or_url}.")

            range_val = request.headers.get("Range", "").strip()
            if range_val.startswith("bytes=") and "-" in range_val:
                range_val = range_val[6:]
                start, end = range_val.split("-")
                if start.isnumeric() and end.isnumeric():
                    start = int(start)
                    end = int(end)
                    response = ranged_response.RangedFileResponse(
                        abs_path,
                        ranged_response.OpenRange(start, end),
                        dict(request.headers),
                        stat_result=os.stat(abs_path),
                    )
                    return response

            return FileResponse(abs_path, headers={"Accept-Ranges": "bytes"})

--- a/gradio/routes.py
+++ b/gradio/routes.py
@@ -1,6 +1,6 @@
         async def file(path_or_url: str, request: fastapi.Request):
             blocks = app.get_blocks()
-            if utils.validate_url(path_or_url):
+            if client_utils.is_http_url_like(path_or_url):
                 return RedirectResponse(
                     url=path_or_url, status_code=status.HTTP_302_FOUND
                 )


DIRAC is a distributed resource framework. In affected versions any user could get a token that has been requested by another user/agent. This may expose resources to unintended parties. This issue has been addressed in release version 8.0.37. Users are advised to upgrade. There are no known workarounds for this vulnerability.

Exposure of Sensitive Information to an Unauthorized Actor

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

# Used to synchronize the cache with user tokens
gTokensSync = ThreadSafe.Synchronizer()

    @classmethod
    def initializeHandler(cls, *args):
        """Initialization

        :return: S_OK()/S_ERROR()
        """
        # Cache containing tokens from scope requested by the client
        cls.__tokensCache = DictCache()

        # The service plays an important OAuth 2.0 role, namely it is an Identity Provider client.
        # This allows you to manage tokens without the involvement of their owners.
        cls.idps = IdProviderFactory()

        # Let's try to connect to the database
        try:
            cls.__tokenDB = TokenDB(parentLogger=cls.log)
        except Exception as e:
            cls.log.exception(e)
            return S_ERROR(f"Could not connect to the database {repr(e)}")

        return S_OK()

    @gTokensSync
    def export_getToken(
        self,
        username: str = None,
        userGroup: str = None,
        scope: list[str] = None,
        audience: str = None,
        identityProvider: str = None,
        requiredTimeLeft: int = 0,
    ):
        """Get an access token for a user/group.

        * Properties:
            * FullDelegation <- permits full delegation of tokens
            * LimitedDelegation <- permits downloading only limited tokens
            * PrivateLimitedDelegation <- permits downloading only limited tokens for one self

        :param username: user name
        :param userGroup: user group
        :param scope: requested scope
        :param audience: requested audience
        :param identityProvider: Identity Provider name
        :param requiredTimeLeft: requested minimum life time

        :return: S_OK(dict)/S_ERROR()
        """
        # Get an IdProvider Client instance
        result = getIdProviderClient(userGroup, identityProvider)
        if not result["OK"]:
            return result
        idpObj = result["Value"]

        # Search for an existing token in tokensCache
        cachedKey = getCachedKey(idpObj, username, userGroup, scope, audience)
        result = getCachedToken(self.__tokensCache, cachedKey, requiredTimeLeft)
        if result["OK"]:
            # A valid token has been found and is returned
            return result

        # A client token is requested
        if not username:
            result = self.__checkProperties("", "")
            if not result["OK"]:
                return result

            # Get the client token with requested scope and audience
            scope = cachedKey[1]
            audience = cachedKey[2]
            result = idpObj.fetchToken(grant_type="client_credentials", scope=scope, audience=audience)
            if not result["OK"]:
                return result
            token = result["Value"]

            # Caching new token: only get an access token (no refresh token in this context)
            self.__tokensCache.add(
                cachedKey,
                result["Value"].get_claim("exp", "access_token") or DEFAULT_AT_EXPIRATION_TIME,
                token,
            )
            return result

        # A user token is requested
        err = []
        # No luck so far, let's refresh the token stored in the database
        result = Registry.getDNForUsername(username)
        if not result["OK"]:
            return result
        for dn in result["Value"]:
            # For backward compatibility, the user ID is written as DN. So let's check if this DN contains a user ID
            result = Registry.getIDFromDN(dn)
            if result["OK"]:
                uid = result["Value"]
                # To do this, first find the refresh token stored in the database with the maximum scope
                result = self.__tokenDB.getTokenForUserProvider(uid, idpObj.name)
                if result["OK"] and result["Value"]:
                    tokens = result["Value"]
                    result = self.__checkProperties(dn, userGroup)
                    if result["OK"]:
                        # refresh token with requested scope
                        result = idpObj.refreshToken(tokens.get("refresh_token"), group=userGroup, scope=scope)
                        if result["OK"]:
                            # caching new tokens
                            self.__tokensCache.add(
                                cachedKey,
                                result["Value"].get_claim("exp", "refresh_token") or DEFAULT_RT_EXPIRATION_TIME,
                                result["Value"],
                            )
                            return result
                # Did not find any token associated with the found user ID
                err.append(result.get("Message", f"No token found for {uid}"))
        # Collect all errors when trying to get a token, or if no user ID is registered
        return S_ERROR("; ".join(err or [f"No user ID found for {username}"]))

    @classmethod
    def initializeHandler(cls, *args):
        """Initialization

        :return: S_OK()/S_ERROR()
        """

        # The service plays an important OAuth 2.0 role, namely it is an Identity Provider client.
        # This allows you to manage tokens without the involvement of their owners.
        cls.idps = IdProviderFactory()

        # Let's try to connect to the database
        try:
            cls.__tokenDB = TokenDB(parentLogger=cls.log)
        except Exception as e:
            cls.log.exception(e)
            return S_ERROR(f"Could not connect to the database {repr(e)}")

        return S_OK()

    def export_getToken(
        self,
        username: str = None,
        userGroup: str = None,
        scope: list[str] = None,
        audience: str = None,
        identityProvider: str = None,
        requiredTimeLeft: int = 0,
    ):
        """Get an access token for a user/group.

        * Properties:
            * FullDelegation <- permits full delegation of tokens
            * LimitedDelegation <- permits downloading only limited tokens
            * PrivateLimitedDelegation <- permits downloading only limited tokens for one self

        :param username: user name
        :param userGroup: user group
        :param scope: requested scope
        :param audience: requested audience
        :param identityProvider: Identity Provider name
        :param requiredTimeLeft: requested minimum life time

        :return: S_OK(dict)/S_ERROR()
        """
        # Get an IdProvider Client instance
        result = getIdProviderClient(userGroup, identityProvider)
        if not result["OK"]:
            return result
        idpObj = result["Value"]

        # getCachedKey is just used here to resolve the default scopes
        _, scope, *_ = getCachedKey(idpObj, username, userGroup, scope, audience)

        # A client token is requested
        if not username:
            result = self.__checkProperties("", "")
            if not result["OK"]:
                return result

            # Get the client token with requested scope and audience
            result = idpObj.fetchToken(grant_type="client_credentials", scope=scope, audience=audience)

            return result

        # A user token is requested
        err = []
        # No luck so far, let's refresh the token stored in the database
        result = Registry.getDNForUsername(username)
        if not result["OK"]:
            return result
        for dn in result["Value"]:
            # For backward compatibility, the user ID is written as DN. So let's check if this DN contains a user ID
            result = Registry.getIDFromDN(dn)
            if result["OK"]:
                uid = result["Value"]
                # To do this, first find the refresh token stored in the database with the maximum scope
                result = self.__tokenDB.getTokenForUserProvider(uid, idpObj.name)
                if result["OK"] and result["Value"]:
                    tokens = result["Value"]
                    result = self.__checkProperties(dn, userGroup)
                    if result["OK"]:
                        # refresh token with requested scope
                        result = idpObj.refreshToken(tokens.get("refresh_token"), group=userGroup, scope=scope)
                        if result["OK"]:
                            return result
                # Did not find any token associated with the found user ID
                err.append(result.get("Message", f"No token found for {uid}"))
        # Collect all errors when trying to get a token, or if no user ID is registered
        return S_ERROR("; ".join(err or [f"No user ID found for {username}"]))

--- a/src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# Used to synchronize the cache with user tokens
-gTokensSync = ThreadSafe.Synchronizer()

--- a/src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py
+++ b/src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py
@@ -4,8 +4,6 @@

         :return: S_OK()/S_ERROR()
         """
-        # Cache containing tokens from scope requested by the client
-        cls.__tokensCache = DictCache()

         # The service plays an important OAuth 2.0 role, namely it is an Identity Provider client.
         # This allows you to manage tokens without the involvement of their owners.

--- a/src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py
+++ b/src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py
@@ -1,4 +1,3 @@
-    @gTokensSync
     def export_getToken(
         self,
         username: str = None,
@@ -30,12 +29,8 @@
             return result
         idpObj = result["Value"]

-        # Search for an existing token in tokensCache
-        cachedKey = getCachedKey(idpObj, username, userGroup, scope, audience)
-        result = getCachedToken(self.__tokensCache, cachedKey, requiredTimeLeft)
-        if result["OK"]:
-            # A valid token has been found and is returned
-            return result
+        # getCachedKey is just used here to resolve the default scopes
+        _, scope, *_ = getCachedKey(idpObj, username, userGroup, scope, audience)

         # A client token is requested
         if not username:
@@ -44,19 +39,8 @@
                 return result

             # Get the client token with requested scope and audience
-            scope = cachedKey[1]
-            audience = cachedKey[2]
             result = idpObj.fetchToken(grant_type="client_credentials", scope=scope, audience=audience)
-            if not result["OK"]:
-                return result
-            token = result["Value"]

-            # Caching new token: only get an access token (no refresh token in this context)
-            self.__tokensCache.add(
-                cachedKey,
-                result["Value"].get_claim("exp", "access_token") or DEFAULT_AT_EXPIRATION_TIME,
-                token,
-            )
             return result

         # A user token is requested
@@ -79,12 +63,6 @@
                         # refresh token with requested scope
                         result = idpObj.refreshToken(tokens.get("refresh_token"), group=userGroup, scope=scope)
                         if result["OK"]:
-                            # caching new tokens
-                            self.__tokensCache.add(
-                                cachedKey,
-                                result["Value"].get_claim("exp", "refresh_token") or DEFAULT_RT_EXPIRATION_TIME,
-                                result["Value"],
-                            )
                             return result
                 # Did not find any token associated with the found user ID
                 err.append(result.get("Message", f"No token found for {uid}"))