In Apache Airflow, prior to version 2.4.1, deactivating a user wouldn't prevent an already authenticated user from being able to continue using the UI or API.

Improper Authorization

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

Execution with Unnecessary Privileges

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

def create_app(config=None, testing=False):
    """Create a new instance of Airflow WWW app"""
    flask_app = Flask(__name__)
    flask_app.secret_key = conf.get('webserver', 'SECRET_KEY')

    flask_app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(minutes=settings.get_session_lifetime_config())
    flask_app.config.from_pyfile(settings.WEBSERVER_CONFIG, silent=True)
    flask_app.config['APP_NAME'] = conf.get(section="webserver", key="instance_name", fallback="Airflow")
    flask_app.config['TESTING'] = testing
    flask_app.config['SQLALCHEMY_DATABASE_URI'] = conf.get('database', 'SQL_ALCHEMY_CONN')

    url = make_url(flask_app.config['SQLALCHEMY_DATABASE_URI'])
    if url.drivername == 'sqlite' and url.database and not url.database.startswith('/'):
        raise AirflowConfigException(
            f'Cannot use relative path: `{conf.get("database", "SQL_ALCHEMY_CONN")}` to connect to sqlite. '
            'Please use absolute path such as `sqlite:////tmp/airflow.db`.'
        )

    flask_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    flask_app.config['SESSION_COOKIE_HTTPONLY'] = True
    flask_app.config['SESSION_COOKIE_SECURE'] = conf.getboolean('webserver', 'COOKIE_SECURE')

    cookie_samesite_config = conf.get('webserver', 'COOKIE_SAMESITE')
    if cookie_samesite_config == "":
        warnings.warn(
            "Old deprecated value found for `cookie_samesite` option in `[webserver]` section. "
            "Using `Lax` instead. Change the value to `Lax` in airflow.cfg to remove this warning.",
            RemovedInAirflow3Warning,
        )
        cookie_samesite_config = "Lax"
    flask_app.config['SESSION_COOKIE_SAMESITE'] = cookie_samesite_config

    if config:
        flask_app.config.from_mapping(config)

    if 'SQLALCHEMY_ENGINE_OPTIONS' not in flask_app.config:
        flask_app.config['SQLALCHEMY_ENGINE_OPTIONS'] = settings.prepare_engine_args()

    # Configure the JSON encoder used by `|tojson` filter from Flask
    flask_app.json_provider_class = AirflowJsonProvider
    flask_app.json = AirflowJsonProvider(flask_app)

    csrf.init_app(flask_app)

    init_wsgi_middleware(flask_app)

    db = SQLA()
    db.session = settings.Session
    db.init_app(flask_app)

    init_dagbag(flask_app)

    init_api_experimental_auth(flask_app)

    init_robots(flask_app)

    cache_config = {'CACHE_TYPE': 'flask_caching.backends.filesystem', 'CACHE_DIR': gettempdir()}
    Cache(app=flask_app, config=cache_config)

    init_flash_views(flask_app)

    configure_logging()
    configure_manifest_files(flask_app)

    import_all_models()

    with flask_app.app_context():
        init_appbuilder(flask_app)

        init_appbuilder_views(flask_app)
        init_appbuilder_links(flask_app)
        init_plugins(flask_app)
        init_connection_form()
        init_error_handlers(flask_app)
        init_api_connexion(flask_app)
        init_api_experimental(flask_app)

        sync_appbuilder_roles(flask_app)

        init_jinja_globals(flask_app)
        init_xframe_protection(flask_app)
        init_airflow_session_interface(flask_app)
    return flask_app

def create_app(config=None, testing=False):
    """Create a new instance of Airflow WWW app"""
    flask_app = Flask(__name__)
    flask_app.secret_key = conf.get('webserver', 'SECRET_KEY')

    flask_app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(minutes=settings.get_session_lifetime_config())
    flask_app.config.from_pyfile(settings.WEBSERVER_CONFIG, silent=True)
    flask_app.config['APP_NAME'] = conf.get(section="webserver", key="instance_name", fallback="Airflow")
    flask_app.config['TESTING'] = testing
    flask_app.config['SQLALCHEMY_DATABASE_URI'] = conf.get('database', 'SQL_ALCHEMY_CONN')

    url = make_url(flask_app.config['SQLALCHEMY_DATABASE_URI'])
    if url.drivername == 'sqlite' and url.database and not url.database.startswith('/'):
        raise AirflowConfigException(
            f'Cannot use relative path: `{conf.get("database", "SQL_ALCHEMY_CONN")}` to connect to sqlite. '
            'Please use absolute path such as `sqlite:////tmp/airflow.db`.'
        )

    flask_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    flask_app.config['SESSION_COOKIE_HTTPONLY'] = True
    flask_app.config['SESSION_COOKIE_SECURE'] = conf.getboolean('webserver', 'COOKIE_SECURE')

    cookie_samesite_config = conf.get('webserver', 'COOKIE_SAMESITE')
    if cookie_samesite_config == "":
        warnings.warn(
            "Old deprecated value found for `cookie_samesite` option in `[webserver]` section. "
            "Using `Lax` instead. Change the value to `Lax` in airflow.cfg to remove this warning.",
            RemovedInAirflow3Warning,
        )
        cookie_samesite_config = "Lax"
    flask_app.config['SESSION_COOKIE_SAMESITE'] = cookie_samesite_config

    if config:
        flask_app.config.from_mapping(config)

    if 'SQLALCHEMY_ENGINE_OPTIONS' not in flask_app.config:
        flask_app.config['SQLALCHEMY_ENGINE_OPTIONS'] = settings.prepare_engine_args()

    # Configure the JSON encoder used by `|tojson` filter from Flask
    flask_app.json_provider_class = AirflowJsonProvider
    flask_app.json = AirflowJsonProvider(flask_app)

    csrf.init_app(flask_app)

    init_wsgi_middleware(flask_app)

    db = SQLA()
    db.session = settings.Session
    db.init_app(flask_app)

    init_dagbag(flask_app)

    init_api_experimental_auth(flask_app)

    init_robots(flask_app)

    cache_config = {'CACHE_TYPE': 'flask_caching.backends.filesystem', 'CACHE_DIR': gettempdir()}
    Cache(app=flask_app, config=cache_config)

    init_flash_views(flask_app)

    configure_logging()
    configure_manifest_files(flask_app)

    import_all_models()

    with flask_app.app_context():
        init_appbuilder(flask_app)

        init_appbuilder_views(flask_app)
        init_appbuilder_links(flask_app)
        init_plugins(flask_app)
        init_connection_form()
        init_error_handlers(flask_app)
        init_api_connexion(flask_app)
        init_api_experimental(flask_app)

        sync_appbuilder_roles(flask_app)

        init_jinja_globals(flask_app)
        init_xframe_protection(flask_app)
        init_airflow_session_interface(flask_app)
        init_check_user_active(flask_app)
    return flask_app

def init_check_user_active(app):
    @app.before_request
    def check_user_active():
        if g.user is not None and not g.user.is_anonymous and not g.user.is_active:
            logout_user()
            return redirect(url_for(app.appbuilder.sm.auth_view.endpoint + ".login"))

--- a/airflow/www/app.py
+++ b/airflow/www/app.py
@@ -81,4 +81,5 @@
         init_jinja_globals(flask_app)
         init_xframe_protection(flask_app)
         init_airflow_session_interface(flask_app)
+        init_check_user_active(flask_app)
     return flask_app

--- /dev/null
+++ b/airflow/www/app.py
@@ -0,0 +1,6 @@
+def init_check_user_active(app):
+    @app.before_request
+    def check_user_active():
+        if g.user is not None and not g.user.is_anonymous and not g.user.is_active:
+            logout_user()
+            return redirect(url_for(app.appbuilder.sm.auth_view.endpoint + ".login"))

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

cd /workspace/airflow
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
/workspace/PoC_env/CVE-2022-41672/bin/python  -m pytest --with-db-init tests/www/views/test_session.py tests/www/views/test_views_base.py -v -k "test_check_active_user or test_index"

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

cd /workspace/airflow
git apply --whitespace=nowarn /workspace/fix.patch
/workspace/PoC_env/CVE-2022-41672/bin/python  -m pytest --with-db-init tests/www/views/test_session.py tests/www/views/test_views_base.py -v -k "not test_index"


Smokescreen is a simple HTTP proxy that fogs over naughty URLs. The primary use case for Smokescreen is to prevent server-side request forgery (SSRF) attacks in which external attackers leverage the behavior of applications to connect to or scan internal infrastructure. Smokescreen also offers an option to deny access to additional (e.g., external) URLs by way of a deny list. There was an issue in Smokescreen that made it possible to bypass the deny list feature by appending a dot to the end of user-supplied URLs, or by providing input in a different letter case. Recommended to upgrade Smokescreen to version 0.0.3 or later.

Server-Side Request Forgery (SSRF)

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

func (acl *ACL) ValidateDomains(domains []string) error {
	for _, d := range domains {
		if d == "" {
			return fmt.Errorf("glob cannot be empty")
		}

		if !strings.HasPrefix(d, "*.") && strings.HasPrefix(d, "*") {
			return fmt.Errorf("%v: domain glob must represent a full prefix (sub)domain", d)
		}

		domainToCheck := strings.TrimPrefix(d, "*")
		if strings.Contains(domainToCheck, "*") {
			return fmt.Errorf("%v: domain globs are only supported as prefix", d)
		}
	}
	return nil
}

func hostMatchesGlob(host string, domainGlob string) bool {
	if domainGlob != "" && domainGlob[0] == '*' {
		suffix := domainGlob[1:]
		if strings.HasSuffix(host, suffix) {
			return true
		}
	} else if domainGlob == host {
		return true
	}
	return false
}

func (acl *ACL) ValidateDomainGlobs(svc string, globs []string) error {
	for _, glob := range globs {
		if glob == "" {
			return fmt.Errorf("glob cannot be empty")
		}

		if glob == "*" || glob == "*." {
			return fmt.Errorf("%v: %v: domain glob must not match everything", svc, glob)
		}

		if !strings.HasPrefix(glob, "*.") && strings.HasPrefix(glob, "*") {
			return fmt.Errorf("%v: %v: domain glob must represent a full prefix (sub)domain", svc, glob)
		}

		domainToCheck := strings.TrimPrefix(glob, "*")
		if strings.Contains(domainToCheck, "*") {
			return fmt.Errorf("%v: %v: domain globs are only supported as prefix", svc, glob)
		}
	}
	return nil
}

func hostMatchesGlob(host string, domainGlob string) bool {
	if host == "" {
		return false
	}

	h := strings.TrimRight(strings.ToLower(host), ".")
	g := strings.TrimRight(strings.ToLower(domainGlob), ".")

	if strings.HasPrefix(g, "*.") {
		suffix := g[1:]
		if strings.HasSuffix(h, suffix) {
			return true
		}
	} else if g == h {
		return true
	}
	return false
}

--- a/pkg/smokescreen/acl/v1/acl.go
+++ b/pkg/smokescreen/acl/v1/acl.go
@@ -1,16 +1,20 @@
-func (acl *ACL) ValidateDomains(domains []string) error {
-	for _, d := range domains {
-		if d == "" {
+func (acl *ACL) ValidateDomainGlobs(svc string, globs []string) error {
+	for _, glob := range globs {
+		if glob == "" {
 			return fmt.Errorf("glob cannot be empty")
 		}

-		if !strings.HasPrefix(d, "*.") && strings.HasPrefix(d, "*") {
-			return fmt.Errorf("%v: domain glob must represent a full prefix (sub)domain", d)
+		if glob == "*" || glob == "*." {
+			return fmt.Errorf("%v: %v: domain glob must not match everything", svc, glob)
 		}

-		domainToCheck := strings.TrimPrefix(d, "*")
+		if !strings.HasPrefix(glob, "*.") && strings.HasPrefix(glob, "*") {
+			return fmt.Errorf("%v: %v: domain glob must represent a full prefix (sub)domain", svc, glob)
+		}
+
+		domainToCheck := strings.TrimPrefix(glob, "*")
 		if strings.Contains(domainToCheck, "*") {
-			return fmt.Errorf("%v: domain globs are only supported as prefix", d)
+			return fmt.Errorf("%v: %v: domain globs are only supported as prefix", svc, glob)
 		}
 	}
 	return nil

--- a/pkg/smokescreen/acl/v1/acl.go
+++ b/pkg/smokescreen/acl/v1/acl.go
@@ -1,10 +1,17 @@
 func hostMatchesGlob(host string, domainGlob string) bool {
-	if domainGlob != "" && domainGlob[0] == '*' {
-		suffix := domainGlob[1:]
-		if strings.HasSuffix(host, suffix) {
+	if host == "" {
+		return false
+	}
+
+	h := strings.TrimRight(strings.ToLower(host), ".")
+	g := strings.TrimRight(strings.ToLower(domainGlob), ".")
+
+	if strings.HasPrefix(g, "*.") {
+		suffix := g[1:]
+		if strings.HasSuffix(h, suffix) {
 			return true
 		}
-	} else if domainGlob == host {
+	} else if g == h {
 		return true
 	}
 	return false

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

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

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

cd /workspace/smokescreen
git apply --whitespace=nowarn /workspace/fix.patch
cd pkg/smokescreen/acl/v1 && go test -timeout 30s -run '^(TestACLDecision|TestACLAddPolicyDisabled|TestACLAddInvalidDomain|TestACLUnknownServiceWithoutDefault|TestACLAddExistingRule|TestACLMalformedPolicyDisable)$'


The shell-quote package before 1.7.3 for Node.js allows command injection. An attacker can inject unescaped shell metacharacters through a regex designed to support Windows drive letters. If the output of this package is passed to a real shell as a quoted argument to a command with exec(), an attacker can inject arbitrary commands. This is because the Windows drive letter regex character class is {A-z] instead of the correct {A-Za-z]. Several shell metacharacters exist in the space between capital letter Z and lower case letter a, such as the backtick character.

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.


A SQL injection issue was discovered in QuerySet.explain() in Django 2.2 before 2.2.28, 3.2 before 3.2.13, and 4.0 before 4.0.4. This occurs by passing a crafted dictionary (with dictionary expansion) as the **options argument, and placing the injection payload in an option name.

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.

    def explain_query_prefix(self, format=None, **options):
        prefix = super().explain_query_prefix(format)
        extra = {}
        if format:
            extra["FORMAT"] = format
        if options:
            extra.update(
                {
                    name.upper(): "true" if value else "false"
                    for name, value in options.items()
                }
            )
        if extra:
            prefix += " (%s)" % ", ".join("%s %s" % i for i in extra.items())
        return prefix

    def explain(self, using, format=None, **options):
        q = self.clone()
        q.explain_info = ExplainInfo(format, options)
        compiler = q.get_compiler(using=using)
        return "\n".join(compiler.explain_query())

    def explain_query_prefix(self, format=None, **options):
        extra = {}
        # Normalize options.
        if options:
            options = {
                name.upper(): "true" if value else "false"
                for name, value in options.items()
            }
            for valid_option in self.explain_options:
                value = options.pop(valid_option, None)
                if value is not None:
                    extra[valid_option.upper()] = value
        prefix = super().explain_query_prefix(format, **options)
        if format:
            extra["FORMAT"] = format
        if extra:
            prefix += " (%s)" % ", ".join("%s %s" % i for i in extra.items())
        return prefix

    def explain(self, using, format=None, **options):
        q = self.clone()
        for option_name in options:
            if (
                not EXPLAIN_OPTIONS_PATTERN.fullmatch(option_name)
                or "--" in option_name
            ):
                raise ValueError(f"Invalid option name: {option_name!r}.")
        q.explain_info = ExplainInfo(format, options)
        compiler = q.get_compiler(using=using)
        return "\n".join(compiler.explain_query())

# Inspired from
# https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
EXPLAIN_OPTIONS_PATTERN = _lazy_re_compile(r"[\w\-]+")

    explain_options = frozenset(
        [
            "ANALYZE",
            "BUFFERS",
            "COSTS",
            "SETTINGS",
            "SUMMARY",
            "TIMING",
            "VERBOSE",
            "WAL",
        ]
    )

--- a/django/db/backends/postgresql/operations.py
+++ b/django/db/backends/postgresql/operations.py
@@ -1,15 +1,18 @@
     def explain_query_prefix(self, format=None, **options):
-        prefix = super().explain_query_prefix(format)
         extra = {}
+        # Normalize options.
+        if options:
+            options = {
+                name.upper(): "true" if value else "false"
+                for name, value in options.items()
+            }
+            for valid_option in self.explain_options:
+                value = options.pop(valid_option, None)
+                if value is not None:
+                    extra[valid_option.upper()] = value
+        prefix = super().explain_query_prefix(format, **options)
         if format:
             extra["FORMAT"] = format
-        if options:
-            extra.update(
-                {
-                    name.upper(): "true" if value else "false"
-                    for name, value in options.items()
-                }
-            )
         if extra:
             prefix += " (%s)" % ", ".join("%s %s" % i for i in extra.items())
         return prefix

--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -1,5 +1,11 @@
     def explain(self, using, format=None, **options):
         q = self.clone()
+        for option_name in options:
+            if (
+                not EXPLAIN_OPTIONS_PATTERN.fullmatch(option_name)
+                or "--" in option_name
+            ):
+                raise ValueError(f"Invalid option name: {option_name!r}.")
         q.explain_info = ExplainInfo(format, options)
         compiler = q.get_compiler(using=using)
         return "\n".join(compiler.explain_query())

--- /dev/null
+++ b/django/db/models/sql/query.py
@@ -0,0 +1,3 @@
+# Inspired from
+# https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
+EXPLAIN_OPTIONS_PATTERN = _lazy_re_compile(r"[\w\-]+")

--- /dev/null
+++ b/django/db/models/sql/query.py
@@ -0,0 +1,12 @@
+    explain_options = frozenset(
+        [
+            "ANALYZE",
+            "BUFFERS",
+            "COSTS",
+            "SETTINGS",
+            "SUMMARY",
+            "TIMING",
+            "VERBOSE",
+            "WAL",
+        ]
+    )

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

cd /workspace/django
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
cd tests && /workspace/PoC_env/CVE-2022-28347/bin/python ./runtests.py queries.test_explain.ExplainTests.test_option_sql_injection queries.test_explain.ExplainTests.test_invalid_option_names

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

cd /workspace/django
git apply --whitespace=nowarn /workspace/fix.patch
cd tests && /workspace/PoC_env/CVE-2022-28347/bin/python ./runtests.py queries.test_explain


parlai is a framework for training and evaluating AI models on a variety of openly available dialogue datasets. In affected versions the package is vulnerable to YAML deserialization attack caused by unsafe loading which leads to Arbitary code execution. This security bug is patched by avoiding unsafe loader users should update to version above v1.1.0. If upgrading is not possible then users can change the Loader used to SafeLoader as a workaround. See commit 507d066ef432ea27d3e201da08009872a2f37725 for details.

Deserialization of Untrusted Data

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

    def _get_shared_models(self, args: "DictConfig") -> Dict[str, dict]:
        with open(args.blueprint.model_opt_path) as f:
            all_model_opts = yaml.load(f.read())
        active_model_opts = {
            model: opt
            for model, opt in all_model_opts.items()
            if self.conversations_needed[model] > 0
        }
        return TurkLikeAgent.get_bot_agents(args=args, model_opts=active_model_opts)

    def _get_shared_models(self, args: "DictConfig") -> Dict[str, dict]:
        with open(args.blueprint.model_opt_path) as f:
            model_opts = yaml.load(f.read())
        return TurkLikeAgent.get_bot_agents(args=args, model_opts=model_opts)

    def _get_shared_models(self, args: "DictConfig") -> Dict[str, dict]:
        with open(args.blueprint.model_opt_path) as f:
            all_model_opts = yaml.safe_load(f.read())
        active_model_opts = {
            model: opt
            for model, opt in all_model_opts.items()
            if self.conversations_needed[model] > 0
        }
        return TurkLikeAgent.get_bot_agents(args=args, model_opts=active_model_opts)

    def _get_shared_models(self, args: "DictConfig") -> Dict[str, dict]:
        with open(args.blueprint.model_opt_path) as f:
            model_opts = yaml.safe_load(f.read())
        return TurkLikeAgent.get_bot_agents(args=args, model_opts=model_opts)

--- a/parlai/crowdsourcing/tasks/model_chat/model_chat_blueprint.py
+++ b/parlai/crowdsourcing/tasks/model_chat/model_chat_blueprint.py
@@ -1,6 +1,6 @@
     def _get_shared_models(self, args: "DictConfig") -> Dict[str, dict]:
         with open(args.blueprint.model_opt_path) as f:
-            all_model_opts = yaml.load(f.read())
+            all_model_opts = yaml.safe_load(f.read())
         active_model_opts = {
             model: opt
             for model, opt in all_model_opts.items()

--- a/parlai/crowdsourcing/tasks/model_chat/model_chat_blueprint.py
+++ b/parlai/crowdsourcing/tasks/model_chat/model_chat_blueprint.py
@@ -1,4 +1,4 @@
     def _get_shared_models(self, args: "DictConfig") -> Dict[str, dict]:
         with open(args.blueprint.model_opt_path) as f:
-            model_opts = yaml.load(f.read())
+            model_opts = yaml.safe_load(f.read())
         return TurkLikeAgent.get_bot_agents(args=args, model_opts=model_opts)


Code Injection in GitHub repository pytorchlightning/pytorch-lightning prior to 1.6.0.

def parse_env_variables(cls: Type["pl.Trainer"], template: str = "PL_%(cls_name)s_%(cls_argument)s") -> Namespace:
    """Parse environment arguments if they are defined.

    Examples:

        >>> from pytorch_lightning import Trainer
        >>> parse_env_variables(Trainer)
        Namespace()
        >>> import os
        >>> os.environ["PL_TRAINER_GPUS"] = '42'
        >>> os.environ["PL_TRAINER_BLABLABLA"] = '1.23'
        >>> parse_env_variables(Trainer)
        Namespace(gpus=42)
        >>> del os.environ["PL_TRAINER_GPUS"]
    """
    cls_arg_defaults = get_init_arguments_and_types(cls)

    env_args = {}
    for arg_name, _, _ in cls_arg_defaults:
        env = template % {"cls_name": cls.__name__.upper(), "cls_argument": arg_name.upper()}
        val = os.environ.get(env)
        if not (val is None or val == ""):
            # todo: specify the possible exception
            with suppress(Exception):
                # converting to native types like int/float/bool
                val = eval(val)
            env_args[arg_name] = val
    return Namespace(**env_args)

def parse_env_variables(cls: Type["pl.Trainer"], template: str = "PL_%(cls_name)s_%(cls_argument)s") -> Namespace:
    """Parse environment arguments if they are defined.

    Examples:

        >>> from pytorch_lightning import Trainer
        >>> parse_env_variables(Trainer)
        Namespace()
        >>> import os
        >>> os.environ["PL_TRAINER_GPUS"] = '42'
        >>> os.environ["PL_TRAINER_BLABLABLA"] = '1.23'
        >>> parse_env_variables(Trainer)
        Namespace(gpus=42)
        >>> del os.environ["PL_TRAINER_GPUS"]
    """
    cls_arg_defaults = get_init_arguments_and_types(cls)

    env_args = {}
    for arg_name, _, _ in cls_arg_defaults:
        env = template % {"cls_name": cls.__name__.upper(), "cls_argument": arg_name.upper()}
        val = os.environ.get(env)
        if not (val is None or val == ""):
            # todo: specify the possible exception
            with suppress(Exception):
                # converting to native types like int/float/bool
                val = literal_eval(val)
            env_args[arg_name] = val
    return Namespace(**env_args)

--- a/pytorch_lightning/utilities/argparse.py
+++ b/pytorch_lightning/utilities/argparse.py
@@ -23,6 +23,6 @@
             # todo: specify the possible exception
             with suppress(Exception):
                 # converting to native types like int/float/bool
-                val = eval(val)
+                val = literal_eval(val)
             env_args[arg_name] = val
     return Namespace(**env_args)


The Requests package before 2.20.0 for Python sends an HTTP Authorization header to an http URI upon receiving a same-hostname https-to-http redirect, which makes it easier for remote attackers to discover credentials by sniffing the network.

Insufficiently Protected Credentials

The product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval.

    def rebuild_auth(self, prepared_request, response):
        """When being redirected we may want to strip authentication from the
        request to avoid leaking credentials. This method intelligently removes
        and reapplies authentication where possible to avoid credential loss.
        """
        headers = prepared_request.headers
        url = prepared_request.url

        if 'Authorization' in headers:
            # If we get redirected to a new host, we should strip out any
            # authentication headers.
            original_parsed = urlparse(response.request.url)
            redirect_parsed = urlparse(url)

            if (original_parsed.hostname != redirect_parsed.hostname):
                del headers['Authorization']

        # .netrc might have more auth for us on our new host.
        new_auth = get_netrc_auth(url) if self.trust_env else None
        if new_auth is not None:
            prepared_request.prepare_auth(new_auth)

        return

    def rebuild_auth(self, prepared_request, response):
        """When being redirected we may want to strip authentication from the
        request to avoid leaking credentials. This method intelligently removes
        and reapplies authentication where possible to avoid credential loss.
        """
        headers = prepared_request.headers
        url = prepared_request.url

        if 'Authorization' in headers and self.should_strip_auth(response.request.url, url):
            # If we get redirected to a new host, we should strip out any
            # authentication headers.
            del headers['Authorization']

        # .netrc might have more auth for us on our new host.
        new_auth = get_netrc_auth(url) if self.trust_env else None
        if new_auth is not None:
            prepared_request.prepare_auth(new_auth)

        return

    def should_strip_auth(self, old_url, new_url):
        """Decide whether Authorization header should be removed when redirecting"""
        old_parsed = urlparse(old_url)
        new_parsed = urlparse(new_url)
        if old_parsed.hostname != new_parsed.hostname:
            return True
        # Special case: allow http -> https redirect when using the standard
        # ports. This isn't specified by RFC 7235, but is kept to avoid
        # breaking backwards compatibility with older versions of requests
        # that allowed any redirects on the same host.
        if (old_parsed.scheme == 'http' and old_parsed.port in (80, None)
                and new_parsed.scheme == 'https' and new_parsed.port in (443, None)):
            return False
        # Standard case: root URI must match
        return old_parsed.port != new_parsed.port or old_parsed.scheme != new_parsed.scheme

--- a/requests/sessions.py
+++ b/requests/sessions.py
@@ -6,14 +6,10 @@
         headers = prepared_request.headers
         url = prepared_request.url

-        if 'Authorization' in headers:
+        if 'Authorization' in headers and self.should_strip_auth(response.request.url, url):
             # If we get redirected to a new host, we should strip out any
             # authentication headers.
-            original_parsed = urlparse(response.request.url)
-            redirect_parsed = urlparse(url)
-
-            if (original_parsed.hostname != redirect_parsed.hostname):
-                del headers['Authorization']
+            del headers['Authorization']

         # .netrc might have more auth for us on our new host.
         new_auth = get_netrc_auth(url) if self.trust_env else None

--- /dev/null
+++ b/requests/sessions.py
@@ -0,0 +1,15 @@
+    def should_strip_auth(self, old_url, new_url):
+        """Decide whether Authorization header should be removed when redirecting"""
+        old_parsed = urlparse(old_url)
+        new_parsed = urlparse(new_url)
+        if old_parsed.hostname != new_parsed.hostname:
+            return True
+        # Special case: allow http -> https redirect when using the standard
+        # ports. This isn't specified by RFC 7235, but is kept to avoid
+        # breaking backwards compatibility with older versions of requests
+        # that allowed any redirects on the same host.
+        if (old_parsed.scheme == 'http' and old_parsed.port in (80, None)
+                and new_parsed.scheme == 'https' and new_parsed.port in (443, None)):
+            return False
+        # Standard case: root URI must match
+        return old_parsed.port != new_parsed.port or old_parsed.scheme != new_parsed.scheme

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

cd /workspace/requests
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
/workspace/PoC_env/CVE-2018-18074/bin/python -m pytest tests/test_requests.py::TestRequests::test_auth_is_retained_for_redirect_on_host  tests/test_requests.py::TestRequests::test_should_strip_auth_http_downgrade tests/test_requests.py::TestRequests::test_should_strip_auth_https_upgrade tests/test_requests.py::TestRequests::test_should_strip_auth_port_change

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

cd /workspace/requests
git apply --whitespace=nowarn /workspace/fix.patch
/workspace/PoC_env/CVE-2018-18074/bin/python -m pytest tests/test_requests.py -k "not test_conflicting_post_params and not test_pyopenssl_redirect and not test_https_warnings and not test_connect_timeout and not test_total_timeout_connect"


An issue in Gaberiele Venturi pandasai v.0.8.0 and before allows a remote attacker to execute arbitrary code via a crafted request to the prompt function.

    def _clean_code(self, code: str) -> str:
        """
        A method to clean the code to prevent malicious code execution

        Args:
            code(str): A python code

        Returns (str): Returns a Clean Code String

        """

        tree = ast.parse(code)

        new_body = []

        # clear recent optional dependencies
        self._additional_dependencies = []

        for node in tree.body:
            if isinstance(node, (ast.Import, ast.ImportFrom)):
                self._check_imports(node)
                continue
            if self._is_df_overwrite(node):
                continue
            new_body.append(node)

        new_tree = ast.Module(body=new_body)
        return astor.to_source(new_tree, pretty_source=lambda x: "".join(x)).strip()

    def _clean_code(self, code: str) -> str:
        """
        A method to clean the code to prevent malicious code execution

        Args:
            code(str): A python code

        Returns (str): Returns a Clean Code String

        """

        tree = ast.parse(code)

        new_body = []

        # clear recent optional dependencies
        self._additional_dependencies = []

        for node in tree.body:
            if isinstance(node, (ast.Import, ast.ImportFrom)):
                self._check_imports(node)
                continue
            if self._is_df_overwrite(node) or self._is_jailbreak(node):
                continue
            new_body.append(node)

        new_tree = ast.Module(body=new_body)
        return astor.to_source(new_tree, pretty_source=lambda x: "".join(x)).strip()

    def _is_jailbreak(self, node: ast.stmt) -> bool:
        """
        Remove jailbreaks from the code to prevent malicious code execution.

        Args:
            node (object): ast.stmt

        Returns (bool):
        """

        DANGEROUS_BUILTINS = ["__subclasses__", "__builtins__", "__import__"]

        for child in ast.walk(node):
            if isinstance(child, ast.Name) and child.id in DANGEROUS_BUILTINS:
                return True

        return False

--- a/pandasai/__init__.py
+++ b/pandasai/__init__.py
@@ -20,7 +20,7 @@
             if isinstance(node, (ast.Import, ast.ImportFrom)):
                 self._check_imports(node)
                 continue
-            if self._is_df_overwrite(node):
+            if self._is_df_overwrite(node) or self._is_jailbreak(node):
                 continue
             new_body.append(node)

--- /dev/null
+++ b/pandasai/__init__.py
@@ -0,0 +1,17 @@
+    def _is_jailbreak(self, node: ast.stmt) -> bool:
+        """
+        Remove jailbreaks from the code to prevent malicious code execution.
+
+        Args:
+            node (object): ast.stmt
+
+        Returns (bool):
+        """
+
+        DANGEROUS_BUILTINS = ["__subclasses__", "__builtins__", "__import__"]
+
+        for child in ast.walk(node):
+            if isinstance(child, ast.Name) and child.id in DANGEROUS_BUILTINS:
+                return True
+
+        return False

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

cd /workspace/pandas-ai
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
/workspace/PoC_env/CVE-2023-39660/bin/python -m pytest tests/test_pandasai.py::TestPandasAI::test_clean_code_removes_jailbreak_code -p no:warning --disable-warnings

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

cd /workspace/pandas-ai
git apply --whitespace=nowarn /workspace/fix.patch
/workspace/PoC_env/CVE-2023-39660/bin/python -m pytest tests/test_pandasai.py -p no:warning --disable-warnings


The Jupyter Server provides the backend (i.e. the core services, APIs, and REST endpoints) for Jupyter web applications. Prior to version 1.15.4, unauthorized actors can access sensitive information from server logs. Anytime a 5xx error is triggered, the auth cookie and other header values are recorded in Jupyter Server logs by default. Considering these logs do not require root access, an attacker can monitor these logs, steal sensitive auth/cookie information, and gain access to the Jupyter server. Jupyter Server version 1.15.4 contains a patch for this issue. There are currently no known workarounds.

Insertion of Sensitive Information into Log File

The product writes sensitive information to a log file.

def log_request(handler):
    """log a bit more information about each request than tornado's default

    - move static file get success to debug-level (reduces noise)
    - get proxied IP instead of proxy IP
    - log referer for redirect and failed requests
    - log user-agent for failed requests
    """
    status = handler.get_status()
    request = handler.request
    try:
        logger = handler.log
    except AttributeError:
        logger = access_log

    if status < 300 or status == 304:
        # Successes (or 304 FOUND) are debug-level
        log_method = logger.debug
    elif status < 400:
        log_method = logger.info
    elif status < 500:
        log_method = logger.warning
    else:
        log_method = logger.error

    request_time = 1000.0 * handler.request.request_time()
    ns = dict(
        status=status,
        method=request.method,
        ip=request.remote_ip,
        uri=request.uri,
        request_time=request_time,
    )
    msg = "{status} {method} {uri} ({ip}) {request_time:.2f}ms"
    if status >= 400:
        # log bad referers
        ns["referer"] = request.headers.get("Referer", "None")
        msg = msg + " referer={referer}"
    if status >= 500 and status != 502:
        # log all headers if it caused an error
        log_method(json.dumps(dict(request.headers), indent=2))
    log_method(msg.format(**ns))
    prometheus_log_method(handler)

def log_request(handler):
    """log a bit more information about each request than tornado's default

    - move static file get success to debug-level (reduces noise)
    - get proxied IP instead of proxy IP
    - log referer for redirect and failed requests
    - log user-agent for failed requests
    """
    status = handler.get_status()
    request = handler.request
    try:
        logger = handler.log
    except AttributeError:
        logger = access_log

    if status < 300 or status == 304:
        # Successes (or 304 FOUND) are debug-level
        log_method = logger.debug
    elif status < 400:
        log_method = logger.info
    elif status < 500:
        log_method = logger.warning
    else:
        log_method = logger.error

    request_time = 1000.0 * handler.request.request_time()
    ns = dict(
        status=status,
        method=request.method,
        ip=request.remote_ip,
        uri=request.uri,
        request_time=request_time,
    )
    msg = "{status} {method} {uri} ({ip}) {request_time:.2f}ms"
    if status >= 400:
        # log bad referers
        ns["referer"] = request.headers.get("Referer", "None")
        msg = msg + " referer={referer}"
    if status >= 500 and status != 502:
        # Log a subset of the headers if it caused an error.
        headers = {}
        for header in ['Host', 'Accept', 'Referer', 'User-Agent']:
            if header in request.headers:
                headers[header] = request.headers[header]
        log_method(json.dumps(headers, indent=2))
    log_method(msg.format(**ns))
    prometheus_log_method(handler)

--- a/jupyter_server/log.py
+++ b/jupyter_server/log.py
@@ -37,7 +37,11 @@
         ns["referer"] = request.headers.get("Referer", "None")
         msg = msg + " referer={referer}"
     if status >= 500 and status != 502:
-        # log all headers if it caused an error
-        log_method(json.dumps(dict(request.headers), indent=2))
+        # Log a subset of the headers if it caused an error.
+        headers = {}
+        for header in ['Host', 'Accept', 'Referer', 'User-Agent']:
+            if header in request.headers:
+                headers[header] = request.headers[header]
+        log_method(json.dumps(headers, indent=2))
     log_method(msg.format(**ns))
     prometheus_log_method(handler)


Due to improper sanitization of user input, HTTPEngine.Handle allows for directory traversal, allowing an attacker to read files outside of the target directory that the server has permission to read.

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 (e *HTTPEngine) Handle(w http.ResponseWriter, r *http.Request) {
	ctx := e.ctxPool.Get().(*Context)
	defer e.releaseContext(ctx)

	// Record access log
	if e.a.settings.AccessLogEnabled {
		ctx.Set(reqStartTimeKey, time.Now())
		defer e.a.accessLog.Log(ctx)
	}

	ctx.Req, ctx.Res = ahttp.AcquireRequest(r), ahttp.AcquireResponseWriter(w)

	// Recovery handling
	defer e.handleRecovery(ctx)

	if e.a.settings.RequestIDEnabled {
		ctx.setRequestID()
	}

	// Load session from request if its `stateful` and subject authentication info.
	if ctx.a.SessionManager().IsStateful() {
		ctx.Subject().Session = ctx.a.SessionManager().GetSession(ctx.Req.Unwrap())
		if ctx.Session().IsKeyExists(KeyViewArgAuthcInfo) {
			populateAuthenticationInfo(ctx.Session().Get(KeyViewArgAuthcInfo).(*authc.AuthenticationInfo), ctx)
		}
	}

	// 'OnRequest' HTTP engine event
	e.publishOnRequestEvent(ctx)

	// Middlewares, interceptors, targeted controller
	if len(e.mwChain) == 0 {
		if e.a.Type() == "websocket" {
			ctx.Log().Error("HTTP engine is not configured. It seems like WebSocket application.")
		} else {
			ctx.Log().Error("'init.go' file introduced in release v0.10; please check your 'app-base-dir/app' " +
				"and then add to your version control")
		}
		ctx.Reply().InternalServerError().Error(newError(ErrGeneric, http.StatusInternalServerError))
	} else {
		e.mwChain[0].Next(ctx)
	}

	e.writeReply(ctx)
}

func (e *HTTPEngine) Handle(w http.ResponseWriter, r *http.Request) {
	ctx := e.ctxPool.Get().(*Context)
	defer e.releaseContext(ctx)

	// Record access log
	if e.a.settings.AccessLogEnabled {
		ctx.Set(reqStartTimeKey, time.Now())
		defer e.a.accessLog.Log(ctx)
	}

	// Path Clean
	r.URL.Path = path.Clean(r.URL.Path)

	ctx.Req, ctx.Res = ahttp.AcquireRequest(r), ahttp.AcquireResponseWriter(w)

	// Recovery handling
	defer e.handleRecovery(ctx)

	if e.a.settings.RequestIDEnabled {
		ctx.setRequestID()
	}

	// Load session from request if its `stateful` and subject authentication info.
	if ctx.a.SessionManager().IsStateful() {
		ctx.Subject().Session = ctx.a.SessionManager().GetSession(ctx.Req.Unwrap())
		if ctx.Session().IsKeyExists(KeyViewArgAuthcInfo) {
			populateAuthenticationInfo(ctx.Session().Get(KeyViewArgAuthcInfo).(*authc.AuthenticationInfo), ctx)
		}
	}

	// 'OnRequest' HTTP engine event
	e.publishOnRequestEvent(ctx)

	// Middlewares, interceptors, targeted controller
	if len(e.mwChain) == 0 {
		if e.a.Type() == "websocket" {
			ctx.Log().Error("HTTP engine is not configured. It seems like WebSocket application.")
		} else {
			ctx.Log().Error("'init.go' file introduced in release v0.10; please check your 'app-base-dir/app' " +
				"and then add to your version control")
		}
		ctx.Reply().InternalServerError().Error(newError(ErrGeneric, http.StatusInternalServerError))
	} else {
		e.mwChain[0].Next(ctx)
	}

	e.writeReply(ctx)
}

--- a/http_engine.go
+++ b/http_engine.go
@@ -7,6 +7,9 @@
 		ctx.Set(reqStartTimeKey, time.Now())
 		defer e.a.accessLog.Log(ctx)
 	}
+
+	// Path Clean
+	r.URL.Path = path.Clean(r.URL.Path)

 	ctx.Req, ctx.Res = ahttp.AcquireRequest(r), ahttp.AcquireResponseWriter(w)


A flaw was found in Buildah (and subsequently Podman Build) which allows containers to mount arbitrary locations on the host filesystem into build containers. A malicious Containerfile can use a dummy image with a symbolic link to the root filesystem as a mount source and cause the mount operation to mount the host root filesystem inside the RUN step. The commands inside the RUN step will then have read-write access to the host filesystem, allowing for full container escape at build time.

func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, store storage.Store, imageMountLabel string, additionalMountPoints map[string]internal.StageMountDetails, workDir string) (specs.Mount, string, error) {
	newMount := specs.Mount{
		Type: define.TypeBind,
	}

	setRelabel := false
	mountReadability := false
	setDest := false
	bindNonRecursive := false
	fromImage := ""

	for _, val := range args {
		argName, argValue, hasArgValue := strings.Cut(val, "=")
		switch argName {
		case "type":
			// This is already processed
			continue
		case "bind-nonrecursive":
			newMount.Options = append(newMount.Options, "bind")
			bindNonRecursive = true
		case "ro", "nosuid", "nodev", "noexec":
			// TODO: detect duplication of these options.
			// (Is this necessary?)
			newMount.Options = append(newMount.Options, argName)
			mountReadability = true
		case "rw", "readwrite":
			newMount.Options = append(newMount.Options, "rw")
			mountReadability = true
		case "readonly":
			// Alias for "ro"
			newMount.Options = append(newMount.Options, "ro")
			mountReadability = true
		case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z", "U", "no-dereference":
			if hasArgValue {
				return newMount, "", fmt.Errorf("%v: %w", val, errBadOptionArg)
			}
			newMount.Options = append(newMount.Options, argName)
		case "from":
			if !hasArgValue {
				return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
			}
			fromImage = argValue
		case "bind-propagation":
			if !hasArgValue {
				return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
			}
			newMount.Options = append(newMount.Options, argValue)
		case "src", "source":
			if !hasArgValue {
				return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
			}
			newMount.Source = argValue
		case "target", "dst", "destination":
			if !hasArgValue {
				return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
			}
			targetPath := argValue
			if !path.IsAbs(targetPath) {
				targetPath = filepath.Join(workDir, targetPath)
			}
			if err := parse.ValidateVolumeCtrDir(targetPath); err != nil {
				return newMount, "", err
			}
			newMount.Destination = targetPath
			setDest = true
		case "relabel":
			if setRelabel {
				return newMount, "", fmt.Errorf("cannot pass 'relabel' option more than once: %w", errBadOptionArg)
			}
			setRelabel = true
			switch argValue {
			case "private":
				newMount.Options = append(newMount.Options, "Z")
			case "shared":
				newMount.Options = append(newMount.Options, "z")
			default:
				return newMount, "", fmt.Errorf("%s mount option must be 'private' or 'shared': %w", argName, errBadMntOption)
			}
		case "consistency":
			// Option for OS X only, has no meaning on other platforms
			// and can thus be safely ignored.
			// See also the handling of the equivalent "delegated" and "cached" in ValidateVolumeOpts
		default:
			return newMount, "", fmt.Errorf("%v: %w", argName, errBadMntOption)
		}
	}

	// default mount readability is always readonly
	if !mountReadability {
		newMount.Options = append(newMount.Options, "ro")
	}

	// Following variable ensures that we return imagename only if we did additional mount
	isImageMounted := false
	if fromImage != "" {
		mountPoint := ""
		if additionalMountPoints != nil {
			if val, ok := additionalMountPoints[fromImage]; ok {
				mountPoint = val.MountPoint
			}
		}
		// if mountPoint of image was not found in additionalMap
		// or additionalMap was nil, try mounting image
		if mountPoint == "" {
			image, err := internalUtil.LookupImage(ctx, store, fromImage)
			if err != nil {
				return newMount, "", err
			}

			mountPoint, err = image.Mount(context.Background(), nil, imageMountLabel)
			if err != nil {
				return newMount, "", err
			}
			isImageMounted = true
		}
		contextDir = mountPoint
	}

	// buildkit parity: default bind option must be `rbind`
	// unless specified
	if !bindNonRecursive {
		newMount.Options = append(newMount.Options, "rbind")
	}

	if !setDest {
		return newMount, fromImage, errBadVolDest
	}

	// buildkit parity: support absolute path for sources from current build context
	if contextDir != "" {
		// path should be /contextDir/specified path
		newMount.Source = filepath.Join(contextDir, filepath.Clean(string(filepath.Separator)+newMount.Source))
	} else {
		// looks like its coming from `build run --mount=type=bind` allow using absolute path
		// error out if no source is set
		if newMount.Source == "" {
			return newMount, "", errBadVolSrc
		}
		if err := parse.ValidateVolumeHostDir(newMount.Source); err != nil {
			return newMount, "", err
		}
	}

	opts, err := parse.ValidateVolumeOpts(newMount.Options)
	if err != nil {
		return newMount, fromImage, err
	}
	newMount.Options = opts

	if !isImageMounted {
		// we don't want any cleanups if image was not mounted explicitly
		// so dont return anything
		fromImage = ""
	}

	return newMount, fromImage, nil
}

func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, store storage.Store, imageMountLabel string, additionalMountPoints map[string]internal.StageMountDetails, workDir string) (specs.Mount, string, error) {
	newMount := specs.Mount{
		Type: define.TypeBind,
	}

	setRelabel := false
	mountReadability := false
	setDest := false
	bindNonRecursive := false
	fromImage := ""

	for _, val := range args {
		argName, argValue, hasArgValue := strings.Cut(val, "=")
		switch argName {
		case "type":
			// This is already processed
			continue
		case "bind-nonrecursive":
			newMount.Options = append(newMount.Options, "bind")
			bindNonRecursive = true
		case "ro", "nosuid", "nodev", "noexec":
			// TODO: detect duplication of these options.
			// (Is this necessary?)
			newMount.Options = append(newMount.Options, argName)
			mountReadability = true
		case "rw", "readwrite":
			newMount.Options = append(newMount.Options, "rw")
			mountReadability = true
		case "readonly":
			// Alias for "ro"
			newMount.Options = append(newMount.Options, "ro")
			mountReadability = true
		case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z", "U", "no-dereference":
			if hasArgValue {
				return newMount, "", fmt.Errorf("%v: %w", val, errBadOptionArg)
			}
			newMount.Options = append(newMount.Options, argName)
		case "from":
			if !hasArgValue {
				return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
			}
			fromImage = argValue
		case "bind-propagation":
			if !hasArgValue {
				return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
			}
			newMount.Options = append(newMount.Options, argValue)
		case "src", "source":
			if !hasArgValue {
				return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
			}
			newMount.Source = argValue
		case "target", "dst", "destination":
			if !hasArgValue {
				return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
			}
			targetPath := argValue
			if !path.IsAbs(targetPath) {
				targetPath = filepath.Join(workDir, targetPath)
			}
			if err := parse.ValidateVolumeCtrDir(targetPath); err != nil {
				return newMount, "", err
			}
			newMount.Destination = targetPath
			setDest = true
		case "relabel":
			if setRelabel {
				return newMount, "", fmt.Errorf("cannot pass 'relabel' option more than once: %w", errBadOptionArg)
			}
			setRelabel = true
			switch argValue {
			case "private":
				newMount.Options = append(newMount.Options, "Z")
			case "shared":
				newMount.Options = append(newMount.Options, "z")
			default:
				return newMount, "", fmt.Errorf("%s mount option must be 'private' or 'shared': %w", argName, errBadMntOption)
			}
		case "consistency":
			// Option for OS X only, has no meaning on other platforms
			// and can thus be safely ignored.
			// See also the handling of the equivalent "delegated" and "cached" in ValidateVolumeOpts
		default:
			return newMount, "", fmt.Errorf("%v: %w", argName, errBadMntOption)
		}
	}

	// default mount readability is always readonly
	if !mountReadability {
		newMount.Options = append(newMount.Options, "ro")
	}

	// Following variable ensures that we return imagename only if we did additional mount
	isImageMounted := false
	if fromImage != "" {
		mountPoint := ""
		if additionalMountPoints != nil {
			if val, ok := additionalMountPoints[fromImage]; ok {
				mountPoint = val.MountPoint
			}
		}
		// if mountPoint of image was not found in additionalMap
		// or additionalMap was nil, try mounting image
		if mountPoint == "" {
			image, err := internalUtil.LookupImage(ctx, store, fromImage)
			if err != nil {
				return newMount, "", err
			}

			mountPoint, err = image.Mount(context.Background(), nil, imageMountLabel)
			if err != nil {
				return newMount, "", err
			}
			isImageMounted = true
		}
		contextDir = mountPoint
	}

	// buildkit parity: default bind option must be `rbind`
	// unless specified
	if !bindNonRecursive {
		newMount.Options = append(newMount.Options, "rbind")
	}

	if !setDest {
		return newMount, fromImage, errBadVolDest
	}

	// buildkit parity: support absolute path for sources from current build context
	if contextDir != "" {
		// path should be /contextDir/specified path
		evaluated, err := copier.Eval(contextDir, newMount.Source, copier.EvalOptions{})
		if err != nil {
			return newMount, "", err
		}
		newMount.Source = evaluated
	} else {
		// looks like its coming from `build run --mount=type=bind` allow using absolute path
		// error out if no source is set
		if newMount.Source == "" {
			return newMount, "", errBadVolSrc
		}
		if err := parse.ValidateVolumeHostDir(newMount.Source); err != nil {
			return newMount, "", err
		}
	}

	opts, err := parse.ValidateVolumeOpts(newMount.Options)
	if err != nil {
		return newMount, fromImage, err
	}
	newMount.Options = opts

	if !isImageMounted {
		// we don't want any cleanups if image was not mounted explicitly
		// so dont return anything
		fromImage = ""
	}

	return newMount, fromImage, nil
}

--- a/internal/volumes/volumes.go
+++ b/internal/volumes/volumes.go
@@ -129,7 +129,11 @@
 	// buildkit parity: support absolute path for sources from current build context
 	if contextDir != "" {
 		// path should be /contextDir/specified path
-		newMount.Source = filepath.Join(contextDir, filepath.Clean(string(filepath.Separator)+newMount.Source))
+		evaluated, err := copier.Eval(contextDir, newMount.Source, copier.EvalOptions{})
+		if err != nil {
+			return newMount, "", err
+		}
+		newMount.Source = evaluated
 	} else {
 		// looks like its coming from `build run --mount=type=bind` allow using absolute path
 		// error out if no source is set


A malicious, but authorised and authenticated user can construct an HTTP request using their existing CSRF token and session cookie to manually upload files to any location that the operating system user account under which pgAdmin is running has permission to write.

    def add(self, req=None):
        """
        File upload functionality
        """
        if not self.validate_request('upload'):
            return self.ERROR_NOT_ALLOWED

        the_dir = self.dir if self.dir is not None else ''
        err_msg = ''
        code = 1
        try:
            path = req.form.get('currentpath')

            file_obj = req.files['newfile']
            file_name = file_obj.filename
            orig_path = "{0}{1}".format(the_dir, path)
            new_name = "{0}{1}".format(orig_path, file_name)

            try:
                # Check if the new file is inside the users directory
                if config.SERVER_MODE:
                    pathlib.Path(new_name).relative_to(the_dir)
            except ValueError:
                return self.ERROR_NOT_ALLOWED

            with open(new_name, 'wb') as f:
                while True:
                    # 4MB chunk (4 * 1024 * 1024 Bytes)
                    data = file_obj.read(4194304)
                    if not data:
                        break
                    f.write(data)
        except Exception as e:
            code = 0
            err_msg = str(e.strerror) if hasattr(e, 'strerror') else str(e)

        try:
            Filemanager.check_access_permission(the_dir, path)
        except Exception as e:
            res = {
                'Error': str(e),
                'Code': 0
            }
            return res

        result = {
            'Path': path,
            'Name': new_name,
            'Error': err_msg,
            'Code': code
        }
        return result

    def add(self, req=None):
        """
        File upload functionality
        """
        if not self.validate_request('upload'):
            return self.ERROR_NOT_ALLOWED

        the_dir = self.dir if self.dir is not None else ''
        err_msg = ''
        code = 1
        try:
            path = req.form.get('currentpath')

            file_obj = req.files['newfile']
            file_name = file_obj.filename
            orig_path = "{0}{1}".format(the_dir, path)
            new_name = "{0}{1}".format(orig_path, file_name)

            try:
                # Check if the new file is inside the users directory
                if config.SERVER_MODE:
                    pathlib.Path(
                        os.path.abspath(
                            os.path.join(the_dir, new_name)
                        )
                    ).relative_to(the_dir)
            except ValueError:
                return self.ERROR_NOT_ALLOWED

            with open(new_name, 'wb') as f:
                while True:
                    # 4MB chunk (4 * 1024 * 1024 Bytes)
                    data = file_obj.read(4194304)
                    if not data:
                        break
                    f.write(data)
        except Exception as e:
            code = 0
            err_msg = str(e.strerror) if hasattr(e, 'strerror') else str(e)

        try:
            Filemanager.check_access_permission(the_dir, path)
        except Exception as e:
            res = {
                'Error': str(e),
                'Code': 0
            }
            return res

        result = {
            'Path': path,
            'Name': new_name,
            'Error': err_msg,
            'Code': code
        }
        return result

--- a/web/pgadmin/misc/file_manager/__init__.py
+++ b/web/pgadmin/misc/file_manager/__init__.py
@@ -19,7 +19,11 @@
             try:
                 # Check if the new file is inside the users directory
                 if config.SERVER_MODE:
-                    pathlib.Path(new_name).relative_to(the_dir)
+                    pathlib.Path(
+                        os.path.abspath(
+                            os.path.join(the_dir, new_name)
+                        )
+                    ).relative_to(the_dir)
             except ValueError:
                 return self.ERROR_NOT_ALLOWED


gitjacker before 0.1.0 allows remote attackers to execute arbitrary code via a crafted .git directory because of directory traversal.

func (r *retriever) downloadFile(path string) error {

	path = strings.TrimSpace(path)

	filePath := filepath.Join(r.outputDir, ".git", path)

	if r.downloaded[path] {
		return nil
	}
	r.downloaded[path] = true

	relative, err := url.Parse(path)
	if err != nil {
		return err
	}

	absolute := r.baseURL.ResolveReference(relative)
	resp, err := r.http.Get(absolute.String())
	if err != nil {
		return fmt.Errorf("failed to retrieve %s: %w", absolute.String(), err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("unexpected status code for url %s : %d", absolute.String(), resp.StatusCode)
	}

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
		return err
	}

	if !strings.HasSuffix(path, "/") {
		if err := ioutil.WriteFile(filePath, content, 0640); err != nil {
			return fmt.Errorf("failed to write %s: %w", filePath, err)
		}
	}

	switch path {
	case "HEAD":
		ref := strings.TrimPrefix(string(content), "ref: ")
		if err := r.downloadFile(ref); err != nil {
			return err
		}
		return nil
	case "config":
		return r.analyseConfig(content)
	case "objects/pack/":
		// parse the directory listing
		packFiles := packLinkRegex.FindAllStringSubmatch(string(content), -1)
		for _, packFile := range packFiles {
			if len(packFile) <= 1 {
				continue
			}
			if err := r.downloadFile(fmt.Sprintf("objects/pack/%s", packFile[1])); err != nil {
				logrus.Debugf("Failed to retrieve pack file %s: %s", packFile[1], err)
				continue
			}
		}
		return nil
	case "objects/info/packs":
		return r.parsePackMetadata(content)
	}

	if strings.HasSuffix(path, ".pack") {
		return r.parsePackFile(path, content)
	}

	if strings.HasPrefix(path, "refs/heads/") {
		if _, err := r.downloadObject(string(content)); err != nil {
			return err
		}
		return nil
	}

	hash := filepath.Base(filepath.Dir(path)) + filepath.Base(path)

	objectType, err := r.getObjectType(hash)
	if err != nil {
		return err
	}

	switch objectType {
	case GitCommitFile:

		commit, err := r.readCommit(hash)
		if err != nil {
			return err
		}

		logrus.Debugf("Successfully retrieved commit %s.", hash)

		if commit.Tree != "" {
			if _, err := r.downloadObject(commit.Tree); err != nil {
				logrus.Debugf("Object %s is missing and likely packed.", commit.Tree)
			}
		}
		for _, parent := range commit.Parents {
			if _, err := r.downloadObject(parent); err != nil {
				logrus.Debugf("Object %s is missing and likely packed.", parent)
			}
		}

	case GitTreeFile:

		tree, err := r.readTree(hash)
		if err != nil {
			return err
		}

		logrus.Debugf("Successfully retrieved tree %s.", hash)

		for _, subHash := range tree.Objects {
			if _, err := r.downloadObject(subHash); err != nil {
				logrus.Debugf("Object %s is missing and likely packed.", subHash)
			}
		}
	case GitBlobFile:
		logrus.Debugf("Successfully retrieved blob %s.", hash)
	default:
		return fmt.Errorf("unknown git file type for %s: %s", path, objectType)
	}

	return nil
}

func (r *retriever) downloadFile(path string) error {

	path = strings.TrimSpace(path)

	filePath := filepath.Join(r.outputDir, ".git", filepath.FromSlash(filepath.Clean("/"+path)))

	if r.downloaded[path] {
		return nil
	}
	r.downloaded[path] = true

	relative, err := url.Parse(path)
	if err != nil {
		return err
	}

	absolute := r.baseURL.ResolveReference(relative)
	resp, err := r.http.Get(absolute.String())
	if err != nil {
		return fmt.Errorf("failed to retrieve %s: %w", absolute.String(), err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("unexpected status code for url %s : %d", absolute.String(), resp.StatusCode)
	}

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
		return err
	}

	if !strings.HasSuffix(path, "/") {
		if err := ioutil.WriteFile(filePath, content, 0640); err != nil {
			return fmt.Errorf("failed to write %s: %w", filePath, err)
		}
	}

	switch path {
	case "HEAD":
		ref := strings.TrimPrefix(string(content), "ref: ")
		if err := r.downloadFile(ref); err != nil {
			return err
		}
		return nil
	case "config":
		return r.analyseConfig(content)
	case "objects/pack/":
		// parse the directory listing
		packFiles := packLinkRegex.FindAllStringSubmatch(string(content), -1)
		for _, packFile := range packFiles {
			if len(packFile) <= 1 {
				continue
			}
			if err := r.downloadFile(fmt.Sprintf("objects/pack/%s", packFile[1])); err != nil {
				logrus.Debugf("Failed to retrieve pack file %s: %s", packFile[1], err)
				continue
			}
		}
		return nil
	case "objects/info/packs":
		return r.parsePackMetadata(content)
	}

	if strings.HasSuffix(path, ".pack") {
		return r.parsePackFile(path, content)
	}

	if strings.HasPrefix(path, "refs/heads/") {
		if _, err := r.downloadObject(string(content)); err != nil {
			return err
		}
		return nil
	}

	hash := filepath.Base(filepath.Dir(path)) + filepath.Base(path)

	objectType, err := r.getObjectType(hash)
	if err != nil {
		return err
	}

	switch objectType {
	case GitCommitFile:

		commit, err := r.readCommit(hash)
		if err != nil {
			return err
		}

		logrus.Debugf("Successfully retrieved commit %s.", hash)

		if commit.Tree != "" {
			if _, err := r.downloadObject(commit.Tree); err != nil {
				logrus.Debugf("Object %s is missing and likely packed.", commit.Tree)
			}
		}
		for _, parent := range commit.Parents {
			if _, err := r.downloadObject(parent); err != nil {
				logrus.Debugf("Object %s is missing and likely packed.", parent)
			}
		}

	case GitTreeFile:

		tree, err := r.readTree(hash)
		if err != nil {
			return err
		}

		logrus.Debugf("Successfully retrieved tree %s.", hash)

		for _, subHash := range tree.Objects {
			if _, err := r.downloadObject(subHash); err != nil {
				logrus.Debugf("Object %s is missing and likely packed.", subHash)
			}
		}
	case GitBlobFile:
		logrus.Debugf("Successfully retrieved blob %s.", hash)
	default:
		return fmt.Errorf("unknown git file type for %s: %s", path, objectType)
	}

	return nil
}

--- a/internal/pkg/gitjacker/retriever.go
+++ b/internal/pkg/gitjacker/retriever.go
@@ -2,7 +2,7 @@

 	path = strings.TrimSpace(path)

-	filePath := filepath.Join(r.outputDir, ".git", path)
+	filePath := filepath.Join(r.outputDir, ".git", filepath.FromSlash(filepath.Clean("/"+path)))

 	if r.downloaded[path] {
 		return nil

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

cd /workspace/gitjacker
rm -rf ./internal/pkg/gitjacker/retriever_test.go
go mod vendor
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
go test   -timeout 30s -run ^TestDownloadFile_PathTraversal$ github.com/liamg/gitjacker/internal/pkg/gitjacker

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

cd /workspace/gitjacker
go mod vendor
git apply --whitespace=nowarn /workspace/fix.patch
cd internal/pkg/gitjacker && go test   -timeout 30s -v github.com/liamg/gitjacker/internal/pkg/gitjacker


PostHog provides open-source product analytics, session recording, feature flagging and A/B testing that you can self-host. A server-side request forgery (SSRF), which can only be exploited by authenticated users, was found in Posthog. Posthog did not verify whether a URL was local when enabling webhooks, allowing authenticated users to forge a POST request. This vulnerability has been addressed in `22bd5942` and will be included in subsequent releases. There are no known workarounds for this vulnerability.

def test_slack_webhook(request):
    """Test webhook."""
    try:
        body = json.loads(request.body)
    except (TypeError, json.decoder.JSONDecodeError):
        return JsonResponse({"error": "Cannot parse request body"}, status=400)

    webhook = body.get("webhook")

    if not webhook:
        return JsonResponse({"error": "no webhook URL"})
    message = {"text": "_Greetings_ from PostHog!"}
    try:
        if is_cloud():  # Protect against SSRF
            raise_if_user_provided_url_unsafe(webhook)
        response = requests.post(webhook, verify=False, json=message)

        if response.ok:
            return JsonResponse({"success": True})
        else:
            return JsonResponse({"error": response.text})
    except:
        return JsonResponse({"error": "invalid webhook URL"})

def test_slack_webhook(request):
    """Test webhook."""
    try:
        body = json.loads(request.body)
    except (TypeError, json.decoder.JSONDecodeError):
        return JsonResponse({"error": "Cannot parse request body"}, status=400)

    webhook = body.get("webhook")

    if not webhook:
        return JsonResponse({"error": "no webhook URL"})
    message = {"text": "_Greetings_ from PostHog!"}
    try:
        if not settings.DEBUG:
            raise_if_user_provided_url_unsafe(webhook)
        response = requests.post(webhook, verify=False, json=message)

        if response.ok:
            return JsonResponse({"success": True})
        else:
            return JsonResponse({"error": response.text})
    except:
        return JsonResponse({"error": "invalid webhook URL"})

--- a/posthog/api/user.py
+++ b/posthog/api/user.py
@@ -11,7 +11,7 @@
         return JsonResponse({"error": "no webhook URL"})
     message = {"text": "_Greetings_ from PostHog!"}
     try:
-        if is_cloud():  # Protect against SSRF
+        if not settings.DEBUG:
             raise_if_user_provided_url_unsafe(webhook)
         response = requests.post(webhook, verify=False, json=message)


Due to improper type validation in attachment parsing the Socket.io js library, it is possible to overwrite the _placeholder object which allows an attacker to place references to functions at arbitrary places in the resulting query object.

function _reconstructPacket(data, buffers) {
  if (!data) return data;

  if (data && data._placeholder) {
    return buffers[data.num]; // appropriate buffer (should be natural order anyway)
  } else if (isArray(data)) {
    for (var i = 0; i < data.length; i++) {
      data[i] = _reconstructPacket(data[i], buffers);
    }
  } else if (typeof data === 'object') {
    for (var key in data) {
      data[key] = _reconstructPacket(data[key], buffers);
    }
  }

  return data;
}

function _reconstructPacket(data, buffers) {
  if (!data) return data;

  if (data && data._placeholder === true) {
    var isIndexValid =
      typeof data.num === "number" &&
      data.num >= 0 &&
      data.num < buffers.length;
    if (isIndexValid) {
      return buffers[data.num]; // appropriate buffer (should be natural order anyway)
    } else {
      throw new Error("illegal attachments");
    }
  } else if (isArray(data)) {
    for (var i = 0; i < data.length; i++) {
      data[i] = _reconstructPacket(data[i], buffers);
    }
  } else if (typeof data === 'object') {
    for (var key in data) {
      data[key] = _reconstructPacket(data[key], buffers);
    }
  }

  return data;
}

--- a/binary.js
+++ b/binary.js
@@ -1,8 +1,16 @@
 function _reconstructPacket(data, buffers) {
   if (!data) return data;

-  if (data && data._placeholder) {
-    return buffers[data.num]; // appropriate buffer (should be natural order anyway)
+  if (data && data._placeholder === true) {
+    var isIndexValid =
+      typeof data.num === "number" &&
+      data.num >= 0 &&
+      data.num < buffers.length;
+    if (isIndexValid) {
+      return buffers[data.num]; // appropriate buffer (should be natural order anyway)
+    } else {
+      throw new Error("illegal attachments");
+    }
   } else if (isArray(data)) {
     for (var i = 0; i < data.length; i++) {
       data[i] = _reconstructPacket(data[i], buffers);

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

cd /workspace/socket.io-parser
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
npx mocha --grep "throws an error when adding an attachment with an invalid"

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

cd /workspace/socket.io-parser
git apply --whitespace=nowarn /workspace/fix.patch
npx mocha test/buffer.js


Versions of the package nemo-appium before 0.0.9 are vulnerable to Command Injection due to improper input sanitization in the 'module.exports.setup' function.

**Note:** In order to exploit this vulnerability appium-running 0.1.3  has to be installed as one of nemo-appium dependencies.

module.exports.setup = function (apath, nemo, cb) {
    var once = function () {
        cb.apply(null, arguments);
        once = function () {
            error('once called more than once');
        };
    };
    //check if appium is already running
    ar(4723, function (success) {
        if (success) {
            log("Appium is running, move on!");
            once(null);
            // run test
        } else {
            log("Appium is not running, exec appium &");
            //start it
            var appiumPath = apath;
            var appiumProcess = cp.exec(appiumPath, function (err) {
                if (err) {
                    error(err);
                    once(err);
                }
            });
            nemo.appium = {
                process: appiumProcess,
                kill: function () {
                    log("attempt to kill appium");
                    appiumProcess.kill();
                }
            };
            appiumProcess.stdout.on('data', function (data) {
                if (data.indexOf('Appium REST http interface listener started on 0.0.0.0:4723') !== -1) {
                    once(null);
                }
                log(data);
            });
            appiumProcess.stderr.on('data', function (data) {
                error(data);
            });
            appiumProcess.on('exit', function (code) {
                log('appium exited with code', code);
            });
        }
    });
};

module.exports.setup = function (apath, nemo, cb) {
    // validate apath since we are going to execute it
    // should end in appium, and contain no spaces
    if (apath.indexOf('appium') !== apath.length - 6 || apath.indexOf(' ') > -1) {
        return cb(new Error('Invalid path to appium executable'))
    }
    var once = function () {
        cb.apply(null, arguments);
        once = function () {
            error('once called more than once');
        };
    };
    //check if appium is already running
    ar(4723, function (success) {
        if (success) {
            log("Appium is running, move on!");
            once(null);
            // run test
        } else {
            log("Appium is not running, exec appium &");
            //start it
            var appiumPath = apath;
            var appiumProcess = cp.exec(appiumPath, function (err) {
                if (err) {
                    error(err);
                    once(err);
                }
            });
            nemo.appium = {
                process: appiumProcess,
                kill: function () {
                    log("attempt to kill appium");
                    appiumProcess.kill();
                }
            };
            appiumProcess.stdout.on('data', function (data) {
                if (data.indexOf('Appium REST http interface listener started on 0.0.0.0:4723') !== -1) {
                    once(null);
                }
                log(data);
            });
            appiumProcess.stderr.on('data', function (data) {
                error(data);
            });
            appiumProcess.on('exit', function (code) {
                log('appium exited with code', code);
            });
        }
    });
};

--- a/index.js
+++ b/index.js
@@ -1,4 +1,9 @@
 module.exports.setup = function (apath, nemo, cb) {
+    // validate apath since we are going to execute it
+    // should end in appium, and contain no spaces
+    if (apath.indexOf('appium') !== apath.length - 6 || apath.indexOf(' ') > -1) {
+        return cb(new Error('Invalid path to appium executable'))
+    }
     var once = function () {
         cb.apply(null, arguments);
         once = function () {


CVAT is an opensource interactive video and image annotation tool for computer vision. Versions prior to 2.0.0 were found to be subject to a Server-side request forgery (SSRF) vulnerability. Validation has been added to urls used in the affected code path in version 2.0.0. Users are advised to upgrade. There are no known workarounds for this issue.

def _download_data(urls, upload_dir):
    job = rq.get_current_job()
    local_files = {}
    for url in urls:
        name = os.path.basename(urlrequest.url2pathname(urlparse.urlparse(url).path))
        if name in local_files:
            raise Exception("filename collision: {}".format(name))
        slogger.glob.info("Downloading: {}".format(url))
        job.meta['status'] = '{} is being downloaded..'.format(url)
        job.save_meta()

        response = requests.get(url, stream=True)
        if response.status_code == 200:
            response.raw.decode_content = True
            with open(os.path.join(upload_dir, name), 'wb') as output_file:
                shutil.copyfileobj(response.raw, output_file)
        else:
            raise Exception("Failed to download " + url)

        local_files[name] = True

    return list(local_files.keys())

def _download_data(urls, upload_dir):
    job = rq.get_current_job()
    local_files = {}
    for url in urls:
        name = os.path.basename(urlrequest.url2pathname(urlparse.urlparse(url).path))
        if name in local_files:
            raise Exception("filename collision: {}".format(name))
        _validate_url(url)
        slogger.glob.info("Downloading: {}".format(url))
        job.meta['status'] = '{} is being downloaded..'.format(url)
        job.save_meta()

        response = requests.get(url, stream=True)
        if response.status_code == 200:
            response.raw.decode_content = True
            with open(os.path.join(upload_dir, name), 'wb') as output_file:
                shutil.copyfileobj(response.raw, output_file)
        else:
            raise Exception("Failed to download " + url)

        local_files[name] = True

    return list(local_files.keys())

--- a/cvat/apps/engine/task.py
+++ b/cvat/apps/engine/task.py
@@ -5,6 +5,7 @@
         name = os.path.basename(urlrequest.url2pathname(urlparse.urlparse(url).path))
         if name in local_files:
             raise Exception("filename collision: {}".format(name))
+        _validate_url(url)
         slogger.glob.info("Downloading: {}".format(url))
         job.meta['status'] = '{} is being downloaded..'.format(url)
         job.save_meta()


A server directory traversal vulnerability was found on node module mcstatic <=0.0.20 that would allow an attack to access sensitive information in the file system by appending slashes in the URL path.

    return function(req,res){
        var root = options.root;
        var mockPaths = getMockPathForMethod(options,req.method);

        var filePath = httpHelpers.getRequestPathFromUrl(req.url);
        var mockedFilePath = findMockFilePath(filePath,mockPaths);
        if(mockedFilePath)
            filePath = mockedFilePath;

        var file = path.normalize(path.join(root,filePath));
        fs.stat(file,function(error, stats){
            if(error)
                return statusHandlers[500](res, nextHandler, { error: error });

            if(req.method === 'HEAD')
                return responseHandlers.handleHead(req,res);

            httpHelpers.setContentType(res,file);
            if (req.headers && req.headers['range'])
                return responseHandlers.handleRange(req, file, stats);

            if(!req.method)
                return statusHandlers[500](res, nextHandler, { error: 'Method not handled' });

            if (req.method === 'GET')
                return responseHandlers.handleGet(res,file, stats, nextHandler);
            if (req.method === 'POST')
                return responseHandlers.handlePost(res,file, stats, nextHandler);
            if (req.method === 'PUT')
                return responseHandlers.handlePut(res,file, stats, nextHandler);
            if (req.method === 'DELETE')
                return responseHandlers.handleDelete(res, file, stats, nextHandler);
        });
    };

module.exports = function staticFileListener(options, nextHandler){
    assert(options.root,'A root must be defined');
    assert(options.getMockPaths,'A get mock path object must be defined');
    assert(options.putMockPaths,'A put mock path object must be defined');
    assert(options.postMockPaths,'A post mock path object must be defined');
    assert(options.deleteMockPaths,'A delete mock path object must be defined');

    return function(req,res){
        var root = options.root;
        var mockPaths = getMockPathForMethod(options,req.method);
        //fixing path traversal issue - CVE-2018-16482
        var filePath = httpHelpers.getRequestPathFromUrl(path.normalize(req.url));
        var mockedFilePath = findMockFilePath(filePath,mockPaths);
        if(mockedFilePath)
            filePath = mockedFilePath;

        var file = path.normalize(path.join(root,filePath));
        fs.stat(file,function(error, stats){
            if(error)
                return statusHandlers[500](res, nextHandler, { error: error });

            if(req.method === 'HEAD')
                return responseHandlers.handleHead(req,res);

            httpHelpers.setContentType(res,file);
            if (req.headers && req.headers['range'])
                return responseHandlers.handleRange(req, file, stats);

            if(!req.method)
                return statusHandlers[500](res, nextHandler, { error: 'Method not handled' });

            if (req.method === 'GET')
                return responseHandlers.handleGet(res,file, stats, nextHandler);
            if (req.method === 'POST')
                return responseHandlers.handlePost(res,file, stats, nextHandler);
            if (req.method === 'PUT')
                return responseHandlers.handlePut(res,file, stats, nextHandler);
            if (req.method === 'DELETE')
                return responseHandlers.handleDelete(res, file, stats, nextHandler);
        });
    };
};

--- a/lib/staticFileHandler.js
+++ b/lib/staticFileHandler.js
@@ -1,8 +1,15 @@
+module.exports = function staticFileListener(options, nextHandler){
+    assert(options.root,'A root must be defined');
+    assert(options.getMockPaths,'A get mock path object must be defined');
+    assert(options.putMockPaths,'A put mock path object must be defined');
+    assert(options.postMockPaths,'A post mock path object must be defined');
+    assert(options.deleteMockPaths,'A delete mock path object must be defined');
+
     return function(req,res){
         var root = options.root;
         var mockPaths = getMockPathForMethod(options,req.method);
-
-        var filePath = httpHelpers.getRequestPathFromUrl(req.url);
+        //fixing path traversal issue - CVE-2018-16482
+        var filePath = httpHelpers.getRequestPathFromUrl(path.normalize(req.url));
         var mockedFilePath = findMockFilePath(filePath,mockPaths);
         if(mockedFilePath)
             filePath = mockedFilePath;
@@ -32,3 +39,4 @@
                 return responseHandlers.handleDelete(res, file, stats, nextHandler);
         });
     };
+};

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

cd /workspace/mcstatic
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
jest ./poc