This affects the package find-my-way before 2.2.5, from 3.0.0 and before 3.0.5. It accepts the Accept-Version' header by default, and if versioned routes are not being used, this could lead to a denial of service. Accept-Version can be used as an unkeyed header in a cache poisoning attack.

Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')

The product acts as an intermediary HTTP agent
         (such as a proxy or firewall) in the data flow between two
         entities such as a client and server, but it does not
         interpret malformed HTTP requests or responses in ways that
         are consistent with how the messages will be processed by
         those entities that are at the ultimate destination.

function Router (opts) {
  if (!(this instanceof Router)) {
    return new Router(opts)
  }
  opts = opts || {}

  if (opts.defaultRoute) {
    assert(typeof opts.defaultRoute === 'function', 'The default route must be a function')
    this.defaultRoute = opts.defaultRoute
  } else {
    this.defaultRoute = null
  }

  if (opts.onBadUrl) {
    assert(typeof opts.onBadUrl === 'function', 'The bad url handler must be a function')
    this.onBadUrl = opts.onBadUrl
  } else {
    this.onBadUrl = null
  }

  this.caseSensitive = opts.caseSensitive === undefined ? true : opts.caseSensitive
  this.ignoreTrailingSlash = opts.ignoreTrailingSlash || false
  this.maxParamLength = opts.maxParamLength || 100
  this.allowUnsafeRegex = opts.allowUnsafeRegex || false
  this.versioning = opts.versioning || acceptVersionStrategy
  this.trees = {}
  this.routes = []
}

Router.prototype._on = function _on (method, path, opts, handler, store) {
  if (Array.isArray(method)) {
    for (var k = 0; k < method.length; k++) {
      this._on(method[k], path, opts, handler, store)
    }
    return
  }

  // method validation
  assert(typeof method === 'string', 'Method should be a string')
  assert(httpMethods.indexOf(method) !== -1, `Method '${method}' is not an http method.`)

  // version validation
  if (opts.version !== undefined) {
    assert(typeof opts.version === 'string', 'Version should be a string')
  }

  const params = []
  var j = 0

  this.routes.push({
    method: method,
    path: path,
    opts: opts,
    handler: handler,
    store: store
  })

  const version = opts.version

  for (var i = 0, len = path.length; i < len; i++) {
    // search for parametric or wildcard routes
    // parametric route
    if (path.charCodeAt(i) === 58) {
      var nodeType = NODE_TYPES.PARAM
      j = i + 1
      var staticPart = path.slice(0, i)

      if (this.caseSensitive === false) {
        staticPart = staticPart.toLowerCase()
      }

      // add the static part of the route to the tree
      this._insert(method, staticPart, NODE_TYPES.STATIC, null, null, null, null, version)

      // isolate the parameter name
      var isRegex = false
      while (i < len && path.charCodeAt(i) !== 47) {
        isRegex = isRegex || path[i] === '('
        if (isRegex) {
          i = getClosingParenthensePosition(path, i) + 1
          break
        } else if (path.charCodeAt(i) !== 45) {
          i++
        } else {
          break
        }
      }

      if (isRegex && (i === len || path.charCodeAt(i) === 47)) {
        nodeType = NODE_TYPES.REGEX
      } else if (i < len && path.charCodeAt(i) !== 47) {
        nodeType = NODE_TYPES.MULTI_PARAM
      }

      var parameter = path.slice(j, i)
      var regex = isRegex ? parameter.slice(parameter.indexOf('('), i) : null
      if (isRegex) {
        regex = new RegExp(regex)
        if (!this.allowUnsafeRegex) {
          assert(isRegexSafe(regex), `The regex '${regex.toString()}' is not safe!`)
        }
      }
      params.push(parameter.slice(0, isRegex ? parameter.indexOf('(') : i))

      path = path.slice(0, j) + path.slice(i)
      i = j
      len = path.length

      // if the path is ended
      if (i === len) {
        var completedPath = path.slice(0, i)
        if (this.caseSensitive === false) {
          completedPath = completedPath.toLowerCase()
        }
        return this._insert(method, completedPath, nodeType, params, handler, store, regex, version)
      }
      // add the parameter and continue with the search
      staticPart = path.slice(0, i)
      if (this.caseSensitive === false) {
        staticPart = staticPart.toLowerCase()
      }
      this._insert(method, staticPart, nodeType, params, null, null, regex, version)

      i--
    // wildcard route
    } else if (path.charCodeAt(i) === 42) {
      this._insert(method, path.slice(0, i), NODE_TYPES.STATIC, null, null, null, null, version)
      // add the wildcard parameter
      params.push('*')
      return this._insert(method, path.slice(0, len), NODE_TYPES.MATCH_ALL, params, handler, store, null, version)
    }
  }

  if (this.caseSensitive === false) {
    path = path.toLowerCase()
  }

  // static route
  this._insert(method, path, NODE_TYPES.STATIC, params, handler, store, null, version)
}

module.exports = {
  storage: SemVerStore,
  deriveVersion: function (req, ctx) {
    return req.headers['accept-version']
  }
}

function Router (opts) {
  if (!(this instanceof Router)) {
    return new Router(opts)
  }
  opts = opts || {}

  if (opts.defaultRoute) {
    assert(typeof opts.defaultRoute === 'function', 'The default route must be a function')
    this.defaultRoute = opts.defaultRoute
  } else {
    this.defaultRoute = null
  }

  if (opts.onBadUrl) {
    assert(typeof opts.onBadUrl === 'function', 'The bad url handler must be a function')
    this.onBadUrl = opts.onBadUrl
  } else {
    this.onBadUrl = null
  }

  this.caseSensitive = opts.caseSensitive === undefined ? true : opts.caseSensitive
  this.ignoreTrailingSlash = opts.ignoreTrailingSlash || false
  this.maxParamLength = opts.maxParamLength || 100
  this.allowUnsafeRegex = opts.allowUnsafeRegex || false
  this.versioning = opts.versioning || acceptVersionStrategy(false)
  this.trees = {}
  this.routes = []
}

Router.prototype._on = function _on (method, path, opts, handler, store) {
  if (Array.isArray(method)) {
    for (var k = 0; k < method.length; k++) {
      this._on(method[k], path, opts, handler, store)
    }
    return
  }

  // method validation
  assert(typeof method === 'string', 'Method should be a string')
  assert(httpMethods.indexOf(method) !== -1, `Method '${method}' is not an http method.`)

  // version validation
  if (opts.version !== undefined) {
    assert(typeof opts.version === 'string', 'Version should be a string')
  }

  const params = []
  var j = 0

  this.routes.push({
    method: method,
    path: path,
    opts: opts,
    handler: handler,
    store: store
  })

  const version = opts.version
  if (version != null && this.versioning.disabled) {
    this.versioning = acceptVersionStrategy(true)
  }

  for (var i = 0, len = path.length; i < len; i++) {
    // search for parametric or wildcard routes
    // parametric route
    if (path.charCodeAt(i) === 58) {
      var nodeType = NODE_TYPES.PARAM
      j = i + 1
      var staticPart = path.slice(0, i)

      if (this.caseSensitive === false) {
        staticPart = staticPart.toLowerCase()
      }

      // add the static part of the route to the tree
      this._insert(method, staticPart, NODE_TYPES.STATIC, null, null, null, null, version)

      // isolate the parameter name
      var isRegex = false
      while (i < len && path.charCodeAt(i) !== 47) {
        isRegex = isRegex || path[i] === '('
        if (isRegex) {
          i = getClosingParenthensePosition(path, i) + 1
          break
        } else if (path.charCodeAt(i) !== 45) {
          i++
        } else {
          break
        }
      }

      if (isRegex && (i === len || path.charCodeAt(i) === 47)) {
        nodeType = NODE_TYPES.REGEX
      } else if (i < len && path.charCodeAt(i) !== 47) {
        nodeType = NODE_TYPES.MULTI_PARAM
      }

      var parameter = path.slice(j, i)
      var regex = isRegex ? parameter.slice(parameter.indexOf('('), i) : null
      if (isRegex) {
        regex = new RegExp(regex)
        if (!this.allowUnsafeRegex) {
          assert(isRegexSafe(regex), `The regex '${regex.toString()}' is not safe!`)
        }
      }
      params.push(parameter.slice(0, isRegex ? parameter.indexOf('(') : i))

      path = path.slice(0, j) + path.slice(i)
      i = j
      len = path.length

      // if the path is ended
      if (i === len) {
        var completedPath = path.slice(0, i)
        if (this.caseSensitive === false) {
          completedPath = completedPath.toLowerCase()
        }
        return this._insert(method, completedPath, nodeType, params, handler, store, regex, version)
      }
      // add the parameter and continue with the search
      staticPart = path.slice(0, i)
      if (this.caseSensitive === false) {
        staticPart = staticPart.toLowerCase()
      }
      this._insert(method, staticPart, nodeType, params, null, null, regex, version)

      i--
    // wildcard route
    } else if (path.charCodeAt(i) === 42) {
      this._insert(method, path.slice(0, i), NODE_TYPES.STATIC, null, null, null, null, version)
      // add the wildcard parameter
      params.push('*')
      return this._insert(method, path.slice(0, len), NODE_TYPES.MATCH_ALL, params, handler, store, null, version)
    }
  }

  if (this.caseSensitive === false) {
    path = path.toLowerCase()
  }

  // static route
  this._insert(method, path, NODE_TYPES.STATIC, params, handler, store, null, version)
}

function build (enabled) {
  if (enabled) {
    return {
      storage: SemVerStore,
      deriveVersion: function (req, ctx) {
        return req.headers['accept-version']
      }
    }
  }
  return {
    storage: SemVerStore,
    deriveVersion: function (req, ctx) {},
    disabled: true
  }
}

module.exports = build

--- a/index.js
+++ b/index.js
@@ -22,7 +22,7 @@
   this.ignoreTrailingSlash = opts.ignoreTrailingSlash || false
   this.maxParamLength = opts.maxParamLength || 100
   this.allowUnsafeRegex = opts.allowUnsafeRegex || false
-  this.versioning = opts.versioning || acceptVersionStrategy
+  this.versioning = opts.versioning || acceptVersionStrategy(false)
   this.trees = {}
   this.routes = []
 }

--- a/index.js
+++ b/index.js
@@ -27,6 +27,9 @@
   })

   const version = opts.version
+  if (version != null && this.versioning.disabled) {
+    this.versioning = acceptVersionStrategy(true)
+  }

   for (var i = 0, len = path.length; i < len; i++) {
     // search for parametric or wildcard routes

--- a/lib/accept-version.js
+++ b/lib/accept-version.js
@@ -1,6 +1,17 @@
-module.exports = {
-  storage: SemVerStore,
-  deriveVersion: function (req, ctx) {
-    return req.headers['accept-version']
+function build (enabled) {
+  if (enabled) {
+    return {
+      storage: SemVerStore,
+      deriveVersion: function (req, ctx) {
+        return req.headers['accept-version']
+      }
+    }
+  }
+  return {
+    storage: SemVerStore,
+    deriveVersion: function (req, ctx) {},
+    disabled: true
   }
 }
+
+module.exports = build

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

cd /workspace/find-my-way
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
node test/version.default-versioning.test.js

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

cd /workspace/find-my-way
git apply --whitespace=nowarn /workspace/fix.patch
npx mocha test/version.default-versioning.test.js


Pydantic is a data validation and settings management using Python type hinting. In affected versions passing either `'infinity'`, `'inf'` or `float('inf')` (or their negatives) to `datetime` or `date` fields causes validation to run forever with 100% CPU usage (on one CPU). Pydantic has been patched with fixes available in the following versions: v1.8.2, v1.7.4, v1.6.2. All these versions are available on pypi(https://pypi.org/project/pydantic/#history), and will be available on conda-forge(https://anaconda.org/conda-forge/pydantic) soon. See the changelog(https://pydantic-docs.helpmanual.io/) for details. If you absolutely can't upgrade, you can work around this risk using a validator(https://pydantic-docs.helpmanual.io/usage/validators/) to catch these values. This is not an ideal solution (in particular you'll need a slightly different function for datetimes), instead of a hack like this you should upgrade pydantic. If you are not using v1.8.x, v1.7.x or v1.6.x and are unable to upgrade to a fixed version of pydantic, please create an issue at https://github.com/samuelcolvin/pydantic/issues requesting a back-port, and we will endeavour to release a patch for earlier versions of pydantic.

Loop with Unreachable Exit Condition ('Infinite Loop')

The product contains an iteration or loop with an exit condition that cannot be reached, i.e., an infinite loop.

def from_unix_seconds(seconds: Union[int, float]) -> datetime:
    while abs(seconds) > MS_WATERSHED:
        seconds /= 1000
    dt = EPOCH + timedelta(seconds=seconds)
    return dt.replace(tzinfo=timezone.utc)

def from_unix_seconds(seconds: Union[int, float]) -> datetime:
    if seconds > MAX_NUMBER:
        return datetime.max
    elif seconds < -MAX_NUMBER:
        return datetime.min

    while abs(seconds) > MS_WATERSHED:
        seconds /= 1000
    dt = EPOCH + timedelta(seconds=seconds)
    return dt.replace(tzinfo=timezone.utc)

# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9
MAX_NUMBER = int(3e20)

--- a/pydantic/datetime_parse.py
+++ b/pydantic/datetime_parse.py
@@ -1,4 +1,9 @@
 def from_unix_seconds(seconds: Union[int, float]) -> datetime:
+    if seconds > MAX_NUMBER:
+        return datetime.max
+    elif seconds < -MAX_NUMBER:
+        return datetime.min
+
     while abs(seconds) > MS_WATERSHED:
         seconds /= 1000
     dt = EPOCH + timedelta(seconds=seconds)

--- /dev/null
+++ b/pydantic/datetime_parse.py
@@ -0,0 +1,2 @@
+# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9
+MAX_NUMBER = int(3e20)


Sentry is a developer-first error tracking and performance monitoring platform. An authenticated user delete the user issue alert notifications for arbitrary users given a know alert ID. A patch was issued to ensure authorization checks are properly scoped on requests to delete user alert notifications. Sentry SaaS users do not need to take any action. Self-Hosted Sentry users should upgrade to version 24.9.0 or higher. There are no known workarounds for this vulnerability.

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 delete(self, request: Request, user: User, notification_option_id: str) -> Response:
        try:
            option = NotificationSettingOption.objects.get(
                id=notification_option_id,
            )
        except NotificationSettingOption.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        option.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    def convert_args(
        self,
        request: Request,
        user_id: int | str | None = None,
        *args,
        notification_option_id: int,
        **kwargs,
    ):
        args, kwargs = super().convert_args(request, user_id, *args, **kwargs)
        user = kwargs["user"]
        try:
            option = NotificationSettingOption.objects.get(id=notification_option_id, user=user)
        except NotificationSettingOption.DoesNotExist:
            raise NotFound(detail="User notification setting does not exist")

        kwargs["notification_setting_option"] = option
        return args, kwargs

    def delete(
        self, request: Request, user: User, notification_setting_option: NotificationSettingOption
    ) -> Response:
        notification_setting_option.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

--- a/src/sentry/api/endpoints/user_notification_settings_options_detail.py
+++ b/src/sentry/api/endpoints/user_notification_settings_options_detail.py
@@ -1,10 +1,17 @@
-    def delete(self, request: Request, user: User, notification_option_id: str) -> Response:
+    def convert_args(
+        self,
+        request: Request,
+        user_id: int | str | None = None,
+        *args,
+        notification_option_id: int,
+        **kwargs,
+    ):
+        args, kwargs = super().convert_args(request, user_id, *args, **kwargs)
+        user = kwargs["user"]
         try:
-            option = NotificationSettingOption.objects.get(
-                id=notification_option_id,
-            )
+            option = NotificationSettingOption.objects.get(id=notification_option_id, user=user)
         except NotificationSettingOption.DoesNotExist:
-            return Response(status=status.HTTP_404_NOT_FOUND)
+            raise NotFound(detail="User notification setting does not exist")

-        option.delete()
-        return Response(status=status.HTTP_204_NO_CONTENT)
+        kwargs["notification_setting_option"] = option
+        return args, kwargs

--- /dev/null
+++ b/src/sentry/api/endpoints/user_notification_settings_options_detail.py
@@ -0,0 +1,5 @@
+    def delete(
+        self, request: Request, user: User, notification_setting_option: NotificationSettingOption
+    ) -> Response:
+        notification_setting_option.delete()
+        return Response(status=status.HTTP_204_NO_CONTENT)


Moment.js is a JavaScript date library for parsing, validating, manipulating, and formatting dates. A path traversal vulnerability impacts npm (server) users of Moment.js between versions 1.0.1 and 2.29.1, especially if a user-provided locale string is directly used to switch moment locale. This problem is patched in 2.29.2, and the patch can be applied to all affected versions. As a workaround, sanitize the user-provided locale name before passing it to Moment.js.

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.

function loadLocale(name) {
    var oldLocale = null,
        aliasedRequire;
    // TODO: Find a better way to register and load all the locales in Node
    if (
        locales[name] === undefined &&
        typeof module !== 'undefined' &&
        module &&
        module.exports
    ) {
        try {
            oldLocale = globalLocale._abbr;
            aliasedRequire = require;
            aliasedRequire('./locale/' + name);
            getSetGlobalLocale(oldLocale);
        } catch (e) {
            // mark as not found to avoid repeating expensive file require call causing high CPU
            // when trying to find en-US, en_US, en-us for every format call
            locales[name] = null; // null means not found
        }
    }
    return locales[name];
}

function loadLocale(name) {
    var oldLocale = null,
        aliasedRequire;
    // TODO: Find a better way to register and load all the locales in Node
    if (
        locales[name] === undefined &&
        typeof module !== 'undefined' &&
        module &&
        module.exports &&
        isLocaleNameSane(name)
    ) {
        try {
            oldLocale = globalLocale._abbr;
            aliasedRequire = require;
            aliasedRequire('./locale/' + name);
            getSetGlobalLocale(oldLocale);
        } catch (e) {
            // mark as not found to avoid repeating expensive file require call causing high CPU
            // when trying to find en-US, en_US, en-us for every format call
            locales[name] = null; // null means not found
        }
    }
    return locales[name];
}

function isLocaleNameSane(name) {
    // Prevent names that look like filesystem paths, i.e contain '/' or '\'
    return name.match('^[^/\\\\]*$') != null;
}

--- a/src/lib/locale/locales.js
+++ b/src/lib/locale/locales.js
@@ -6,7 +6,8 @@
         locales[name] === undefined &&
         typeof module !== 'undefined' &&
         module &&
-        module.exports
+        module.exports &&
+        isLocaleNameSane(name)
     ) {
         try {
             oldLocale = globalLocale._abbr;

--- /dev/null
+++ b/src/lib/locale/locales.js
@@ -0,0 +1,4 @@
+function isLocaleNameSane(name) {
+    // Prevent names that look like filesystem paths, i.e contain '/' or '\'
+    return name.match('^[^/\\\\]*$') != null;
+}


Improper Access Control in Pypi calibreweb prior to 0.6.16.

Improper Access Control

The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.

def create_edit_shelf(shelf, page_title, page, shelf_id=False):
    sync_only_selected_shelves = current_user.kobo_only_shelves_sync
    # calibre_db.session.query(ub.Shelf).filter(ub.Shelf.user_id == current_user.id).filter(ub.Shelf.kobo_sync).count()
    if request.method == "POST":
        to_save = request.form.to_dict()
        if not current_user.role_edit_shelfs() and to_save.get("is_public") == "on":
            flash(_(u"Sorry you are not allowed to create a public shelf"), category="error")
            return redirect(url_for('web.index'))
        is_public = 1 if to_save.get("is_public") else 0
        if config.config_kobo_sync:
            shelf.kobo_sync = True if to_save.get("kobo_sync") else False
            if shelf.kobo_sync:
                ub.session.query(ub.ShelfArchive).filter(ub.ShelfArchive.user_id == current_user.id).filter(
                    ub.ShelfArchive.uuid == shelf.uuid).delete()
                ub.session_commit()
        shelf_title = to_save.get("title", "")
        if check_shelf_is_unique(shelf, shelf_title, is_public, shelf_id):
            shelf.name = shelf_title
            shelf.is_public = is_public
            if not shelf_id:
                shelf.user_id = int(current_user.id)
                ub.session.add(shelf)
                shelf_action = "created"
                flash_text = _(u"Shelf %(title)s created", title=shelf_title)
            else:
                shelf_action = "changed"
                flash_text = _(u"Shelf %(title)s changed", title=shelf_title)
            try:
                ub.session.commit()
                log.info(u"Shelf {} {}".format(shelf_title, shelf_action))
                flash(flash_text, category="success")
                return redirect(url_for('shelf.show_shelf', shelf_id=shelf.id))
            except (OperationalError, InvalidRequestError) as ex:
                ub.session.rollback()
                log.debug_or_exception(ex)
                log.error("Settings DB is not Writeable")
                flash(_("Settings DB is not Writeable"), category="error")
            except Exception as ex:
                ub.session.rollback()
                log.debug_or_exception(ex)
                flash(_(u"There was an error"), category="error")
    return render_title_template('shelf_edit.html',
                                 shelf=shelf,
                                 title=page_title,
                                 page=page,
                                 kobo_sync_enabled=config.config_kobo_sync,
                                 sync_only_selected_shelves=sync_only_selected_shelves)

def create_edit_shelf(shelf, page_title, page, shelf_id=False):
    sync_only_selected_shelves = current_user.kobo_only_shelves_sync
    # calibre_db.session.query(ub.Shelf).filter(ub.Shelf.user_id == current_user.id).filter(ub.Shelf.kobo_sync).count()
    if request.method == "POST":
        to_save = request.form.to_dict()
        if not current_user.role_edit_shelfs() and to_save.get("is_public") == "on":
            flash(_(u"Sorry you are not allowed to create a public shelf"), category="error")
            return redirect(url_for('web.index'))
        is_public = 1 if to_save.get("is_public") == "on" else 0
        if config.config_kobo_sync:
            shelf.kobo_sync = True if to_save.get("kobo_sync") else False
            if shelf.kobo_sync:
                ub.session.query(ub.ShelfArchive).filter(ub.ShelfArchive.user_id == current_user.id).filter(
                    ub.ShelfArchive.uuid == shelf.uuid).delete()
                ub.session_commit()
        shelf_title = to_save.get("title", "")
        if check_shelf_is_unique(shelf, shelf_title, is_public, shelf_id):
            shelf.name = shelf_title
            shelf.is_public = is_public
            if not shelf_id:
                shelf.user_id = int(current_user.id)
                ub.session.add(shelf)
                shelf_action = "created"
                flash_text = _(u"Shelf %(title)s created", title=shelf_title)
            else:
                shelf_action = "changed"
                flash_text = _(u"Shelf %(title)s changed", title=shelf_title)
            try:
                ub.session.commit()
                log.info(u"Shelf {} {}".format(shelf_title, shelf_action))
                flash(flash_text, category="success")
                return redirect(url_for('shelf.show_shelf', shelf_id=shelf.id))
            except (OperationalError, InvalidRequestError) as ex:
                ub.session.rollback()
                log.debug_or_exception(ex)
                log.error("Settings DB is not Writeable")
                flash(_("Settings DB is not Writeable"), category="error")
            except Exception as ex:
                ub.session.rollback()
                log.debug_or_exception(ex)
                flash(_(u"There was an error"), category="error")
    return render_title_template('shelf_edit.html',
                                 shelf=shelf,
                                 title=page_title,
                                 page=page,
                                 kobo_sync_enabled=config.config_kobo_sync,
                                 sync_only_selected_shelves=sync_only_selected_shelves)

--- a/cps/shelf.py
+++ b/cps/shelf.py
@@ -6,7 +6,7 @@
         if not current_user.role_edit_shelfs() and to_save.get("is_public") == "on":
             flash(_(u"Sorry you are not allowed to create a public shelf"), category="error")
             return redirect(url_for('web.index'))
-        is_public = 1 if to_save.get("is_public") else 0
+        is_public = 1 if to_save.get("is_public") == "on" else 0
         if config.config_kobo_sync:
             shelf.kobo_sync = True if to_save.get("kobo_sync") else False
             if shelf.kobo_sync:


Concorde, formerly know as Nexkey, is a fork of the federated microblogging platform Misskey. Prior to version 12.25Q1.1, due to an improper implementation of the logout process, authentication credentials remain in cookies even after a user has explicitly logged out, which may allow an attacker to steal authentication tokens. This could have devastating consequences if a user with admin privileges is (or was) using a shared device. Users who have logged in on a shared device should go to Settings > Security and regenerate their login tokens. Version 12.25Q1.1 fixes the issue. As a workaround, clear cookies and site data in the browser after logging out.

Insufficient Session Expiration

According to WASC, "Insufficient Session Expiration is when a web site permits an attacker to reuse old session credentials or session IDs for authorization."

export async function signout() {
    waiting();
    localStorage.removeItem("account");

    await removeAccount($i.id);

    const accounts = await getAccounts();

    //#region Remove service worker registration
    try {
        if (navigator.serviceWorker.controller) {
            const registration = await navigator.serviceWorker.ready;
            const push = await registration.pushManager.getSubscription();
            if (push) {
                await fetch(`${apiUrl}/sw/unregister`, {
                    method: "POST",
                    body: JSON.stringify({
                        i: $i.token,
                        endpoint: push.endpoint,
                    }),
                });
            }
        }

        if (accounts.length === 0) {
            await navigator.serviceWorker.getRegistrations()
                .then(registrations => {
				    return Promise.all(registrations.map(registration => registration.unregister()));
                });
        }
    } catch (err) {
        console.error(err);
    }
    //#endregion

    document.cookie = "igi=; path=/";

    if (accounts.length > 0) login(accounts[0].token);
    else unisonReload("/");
}

export async function getAccounts(): Promise<{ id: Account["id"], token: Account["token"] }[]> {
    return (await get("accounts")) || [];
}

export async function signout() {
    waiting();
    document.cookie.split(";").forEach((cookie) => {
        const cookieName = cookie.split("=")[0].trim();
        if (cookieName === "token") {
            document.cookie = `${cookieName}=; max-age=0; path=/`;
        }
    });
    localStorage.removeItem("account");

    await removeAccount($i.id);

    const accounts = await getAccounts();

    //#region Remove service worker registration
    try {
        if (navigator.serviceWorker.controller) {
            const registration = await navigator.serviceWorker.ready;
            const push = await registration.pushManager.getSubscription();
            if (push) {
                await fetch(`${apiUrl}/sw/unregister`, {
                    method: "POST",
                    body: JSON.stringify({
                        i: $i.token,
                        endpoint: push.endpoint,
                    }),
                });
            }
        }

        if (accounts.length === 0) {
            await navigator.serviceWorker.getRegistrations()
                .then(registrations => {
				    return Promise.all(registrations.map(registration => registration.unregister()));
                });
        }
    } catch (err) {
        console.error(err);
    }
    //#endregion

    document.cookie = "igi=; path=/";

    if (accounts.length > 0) login(accounts[0].token);
    else unisonReload("/");
}

export async function getAccounts(): Promise<{ id: Account["id"], token: Account["token"] }[]> {
    return (await get("accounts")) || [];
}

--- a/packages/client/src/account.ts
+++ b/packages/client/src/account.ts
@@ -1,5 +1,11 @@
 export async function signout() {
     waiting();
+    document.cookie.split(";").forEach((cookie) => {
+        const cookieName = cookie.split("=")[0].trim();
+        if (cookieName === "token") {
+            document.cookie = `${cookieName}=; max-age=0; path=/`;
+        }
+    });
     localStorage.removeItem("account");

     await removeAccount($i.id);


gin-vue-admin is a backstage management system based on vue and gin, which separates the front and rear of the full stack. gin-vue-admin pseudoversion 0.0.0-20240407133540-7bc7c3051067, corresponding to version 2.6.1, has a code injection vulnerability in the backend. In the Plugin System -> Plugin Template feature, an attacker can perform directory traversal by manipulating the `plugName` parameter. They can create specific folders such as `api`, `config`, `global`, `model`, `router`, `service`, and `main.go` function within the specified traversal directory. Moreover, the Go files within these folders can have arbitrary code inserted based on a specific PoC parameter. The main reason for the existence of this vulnerability is the controllability of the PlugName field within the struct. Pseudoversion 0.0.0-20240409100909-b1b7427c6ea6, corresponding to commit b1b7427c6ea6c7a027fa188c6be557f3795e732b, contains a patch for the issue. As a workaround, one may manually use a filtering method available in the GitHub Security Advisory to rectify the directory traversal problem.

func (autoApi *AutoCodeApi) AutoPlug(c *gin.Context) {
	var a system.AutoPlugReq
	err := c.ShouldBindJSON(&a)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	a.Snake = strings.ToLower(a.PlugName)
	a.NeedModel = a.HasRequest || a.HasResponse
	err = autoCodeService.CreatePlug(a)
	if err != nil {
		global.GVA_LOG.Error("\u9884\u89c8\u5931\u8d25!", zap.Error(err))
		response.FailWithMessage("\u9884\u89c8\u5931\u8d25", c)
		return
	}
	response.Ok(c)
}

func (autoApi *AutoCodeApi) AutoPlug(c *gin.Context) {
	var a system.AutoPlugReq
	err := c.ShouldBindJSON(&a)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	if strings.Contains(a.PlugName, string(filepath.Separator)) {
		response.FailWithMessage("\u63d2\u4ef6\u540d\u79f0\u4e0d\u80fd\u5305\u542b"+string(filepath.Separator), c)
		return
	}

	a.Snake = strings.ToLower(a.PlugName)
	a.NeedModel = a.HasRequest || a.HasResponse
	err = autoCodeService.CreatePlug(a)
	if err != nil {
		global.GVA_LOG.Error("\u9884\u89c8\u5931\u8d25!", zap.Error(err))
		response.FailWithMessage("\u9884\u89c8\u5931\u8d25", c)
		return
	}
	response.Ok(c)
}

--- a/server/api/v1/system/sys_auto_code.go
+++ b/server/api/v1/system/sys_auto_code.go
@@ -5,6 +5,11 @@
 		response.FailWithMessage(err.Error(), c)
 		return
 	}
+	if strings.Contains(a.PlugName, string(filepath.Separator)) {
+		response.FailWithMessage("\u63d2\u4ef6\u540d\u79f0\u4e0d\u80fd\u5305\u542b"+string(filepath.Separator), c)
+		return
+	}
+
 	a.Snake = strings.ToLower(a.PlugName)
 	a.NeedModel = a.HasRequest || a.HasResponse
 	err = autoCodeService.CreatePlug(a)


In ginadmin through 05-10-2022, the incoming path value is not filtered, resulting in arbitrary file reading.

func (con adminSystemController) GetDir(c *gin.Context) {

	type FileNode struct {
		Name string `json:"name"`
		Path string `json:"path"`
		Type string `json:"type"`
	}

	var (
		path      string
		err       error
		fileSlice []FileNode
		files     []fs.FileInfo
	)

	fileSlice = make([]FileNode, 0)
	path = gstrings.JoinStr(configs.RootPath, c.Query("path"))

	files, err = ioutil.ReadDir(path)
	if err != nil {
		con.Error(c, "\u83b7\u53d6\u76ee\u5f55\u5931\u8d25")
		return
	}

	for _, v := range files {
		var fileType string
		if v.IsDir() {
			fileType = "dir"
		} else {
			fileType = "file"
		}
		fileSlice = append(fileSlice, FileNode{
			Name: v.Name(),
			Path: gstrings.JoinStr(c.Query("path"), string(filepath.Separator), v.Name()),
			Type: fileType,
		})
	}

	c.JSON(http.StatusOK, gin.H{
		"data": fileSlice,
	})
}

func (con adminSystemController) View(c *gin.Context) {

	var (
		err       error
		startLine int
		endLine   int
		scanner   *bufio.Scanner
		line      int
	)

	startLine, err = strconv.Atoi(c.DefaultQuery("start_line", "1"))
	if err != nil {
		con.ErrorHtml(c, err)
		return
	}
	endLine, err = strconv.Atoi(c.DefaultQuery("end_line", "20"))
	if err != nil {
		con.ErrorHtml(c, err)
		return
	}

	var filecontents []string
	filePath := gstrings.JoinStr(configs.RootPath, c.Query("path"))
	fi, err := os.Open(filePath)
	if err != nil {
		con.ErrorHtml(c, err)
		return
	}
	defer fi.Close()

	scanner = bufio.NewScanner(fi)
	for scanner.Scan() {
		line++
		if line >= startLine && line <= endLine {
			// \u5728\u8981\u6c42\u884c\u6570\u5185\u53d6\u5f97\u6570\u636e
			filecontents = append(filecontents, scanner.Text())
		} else {
			continue
		}
	}

	c.HTML(http.StatusOK, "setting/systemlog_view.html", gin.H{
		"file_path":    c.Query("path"),
		"filecontents": filecontents,
		"start_line":   startLine,
		"end_line":     endLine,
		"line":         line,
	})

}

func JoinStr(items ...interface{}) string {
	if len(items) == 0 {
		return ""
	}
	var builder strings.Builder
	for _, v := range items {
		builder.WriteString(v.(string))
	}
	return builder.String()
}

func (con adminSystemController) GetDir(c *gin.Context) {

	type FileNode struct {
		Name string `json:"name"`
		Path string `json:"path"`
		Type string `json:"type"`
	}

	var (
		path      string
		err       error
		fileSlice []FileNode
		files     []fs.FileInfo
	)

	fileSlice = make([]FileNode, 0)
	path, err = filesystem.FilterPath(configs.RootPath+"logs", c.Query("path"))
	if err != nil {
		con.Error(c, err.Error())
		return
	}

	files, err = ioutil.ReadDir(path)
	if err != nil {
		con.Error(c, "\u83b7\u53d6\u76ee\u5f55\u5931\u8d25")
		return
	}

	for _, v := range files {
		var fileType string
		if v.IsDir() {
			fileType = "dir"
		} else {
			fileType = "file"
		}
		fileSlice = append(fileSlice, FileNode{
			Name: v.Name(),
			Path: gstrings.JoinStr(c.Query("path"), string(filepath.Separator), v.Name()),
			Type: fileType,
		})
	}

	c.JSON(http.StatusOK, gin.H{
		"data": fileSlice,
	})
}

func (con adminSystemController) View(c *gin.Context) {

	var (
		err       error
		startLine int
		endLine   int
		scanner   *bufio.Scanner
		line      int
	)

	startLine, err = strconv.Atoi(c.DefaultQuery("start_line", "1"))
	if err != nil {
		con.ErrorHtml(c, err)
		return
	}
	endLine, err = strconv.Atoi(c.DefaultQuery("end_line", "20"))
	if err != nil {
		con.ErrorHtml(c, err)
		return
	}

	var filecontents []string
	filePath, err := filesystem.FilterPath(configs.RootPath+"logs", c.Query("path"))
	if err != nil {
		con.ErrorHtml(c, err)
		return
	}

	fi, err := os.Open(filePath)
	if err != nil {
		con.ErrorHtml(c, err)
		return
	}
	defer fi.Close()

	scanner = bufio.NewScanner(fi)
	for scanner.Scan() {
		line++
		if line >= startLine && line <= endLine {
			// \u5728\u8981\u6c42\u884c\u6570\u5185\u53d6\u5f97\u6570\u636e
			filecontents = append(filecontents, scanner.Text())
		} else {
			continue
		}
	}

	c.HTML(http.StatusOK, "setting/systemlog_view.html", gin.H{
		"file_path":    c.Query("path"),
		"filecontents": filecontents,
		"start_line":   startLine,
		"end_line":     endLine,
		"line":         line,
	})

}

func FilterPath(root, path string) (string, error) {

	newPath := fmt.Sprintf("%s%s", root, path)
	absPath, err := filepath.Abs(newPath)
	if err != nil {
		return "", err
	}

	absPath = filepath.FromSlash(absPath)
	ifOver := filepath.HasPrefix(absPath, filepath.FromSlash(root))
	fmt.Println(absPath)
	fmt.Println(filepath.FromSlash(root))
	if !ifOver {
		return "", errors.New("access to the path is prohibited")
	}

	return absPath, nil
}

--- a/internal/controllers/admin/setting/adminSystemController.go
+++ b/internal/controllers/admin/setting/adminSystemController.go
@@ -14,7 +14,11 @@
 	)

 	fileSlice = make([]FileNode, 0)
-	path = gstrings.JoinStr(configs.RootPath, c.Query("path"))
+	path, err = filesystem.FilterPath(configs.RootPath+"logs", c.Query("path"))
+	if err != nil {
+		con.Error(c, err.Error())
+		return
+	}

 	files, err = ioutil.ReadDir(path)
 	if err != nil {

--- a/internal/controllers/admin/setting/adminSystemController.go
+++ b/internal/controllers/admin/setting/adminSystemController.go
@@ -20,7 +20,12 @@
 	}

 	var filecontents []string
-	filePath := gstrings.JoinStr(configs.RootPath, c.Query("path"))
+	filePath, err := filesystem.FilterPath(configs.RootPath+"logs", c.Query("path"))
+	if err != nil {
+		con.ErrorHtml(c, err)
+		return
+	}
+
 	fi, err := os.Open(filePath)
 	if err != nil {
 		con.ErrorHtml(c, err)

--- a/pkg/utils/filesystem/filesystem.go
+++ b/pkg/utils/filesystem/filesystem.go
@@ -1,10 +1,18 @@
-func JoinStr(items ...interface{}) string {
-	if len(items) == 0 {
-		return ""
+func FilterPath(root, path string) (string, error) {
+
+	newPath := fmt.Sprintf("%s%s", root, path)
+	absPath, err := filepath.Abs(newPath)
+	if err != nil {
+		return "", err
 	}
-	var builder strings.Builder
-	for _, v := range items {
-		builder.WriteString(v.(string))
+
+	absPath = filepath.FromSlash(absPath)
+	ifOver := filepath.HasPrefix(absPath, filepath.FromSlash(root))
+	fmt.Println(absPath)
+	fmt.Println(filepath.FromSlash(root))
+	if !ifOver {
+		return "", errors.New("access to the path is prohibited")
 	}
-	return builder.String()
+
+	return absPath, nil
 }


Express OpenID Connect is express JS middleware implementing sign on for Express web apps using OpenID Connect. Versions before and including `2.5.1` do not regenerate the session id and session cookie when user logs in. This behavior opens up the application to various session fixation vulnerabilities. Versions `2.5.2` contains a patch for this issue.

Session Fixation

Authenticating a user, or otherwise establishing a new user session, without invalidating any existing session identifier gives an attacker the opportunity to steal authenticated sessions.

      async (req, res, next) => {
        next = cb(next).once();

        client =
          client ||
          (await getClient(config).catch((err) => {
            next(err);
          }));

        if (!client) {
          return;
        }

        try {
          const redirectUri = res.oidc.getRedirectUri();

          let session;

          try {
            const callbackParams = client.callbackParams(req);
            const authVerification = transient.getOnce(
              'auth_verification',
              req,
              res
            );

            const { max_age, code_verifier, nonce, state } = authVerification
              ? JSON.parse(authVerification)
              : {};

            req.openidState = decodeState(state);
            const checks = {
              max_age,
              code_verifier,
              nonce,
              state,
            };

            let extras;
            if (config.tokenEndpointParams) {
              extras = { exchangeBody: config.tokenEndpointParams };
            }

            session = await client.callback(
              redirectUri,
              callbackParams,
              checks,
              extras
            );
          } catch (err) {
            throw createError.BadRequest(err.message);
          }

          if (config.afterCallback) {
            session = await config.afterCallback(
              req,
              res,
              Object.assign({}, session), // Remove non-enumerable methods from the TokenSet
              req.openidState
            );
          }

          Object.assign(req[config.session.name], session);
          attemptSilentLogin.resumeSilentLogin(req, res);

          next();
        } catch (err) {
          // Swallow errors if this is a silentLogin
          if (req.openidState && req.openidState.attemptingSilentLogin) {
            next();
          } else {
            next(err);
          }
        }
      },

      async (req, res, next) => {
        next = cb(next).once();

        client =
          client ||
          (await getClient(config).catch((err) => {
            next(err);
          }));

        if (!client) {
          return;
        }

        try {
          const redirectUri = res.oidc.getRedirectUri();

          let tokenSet;

          try {
            const callbackParams = client.callbackParams(req);
            const authVerification = transient.getOnce(
              'auth_verification',
              req,
              res
            );

            const { max_age, code_verifier, nonce, state } = authVerification
              ? JSON.parse(authVerification)
              : {};

            req.openidState = decodeState(state);
            const checks = {
              max_age,
              code_verifier,
              nonce,
              state,
            };

            let extras;
            if (config.tokenEndpointParams) {
              extras = { exchangeBody: config.tokenEndpointParams };
            }

            tokenSet = await client.callback(
              redirectUri,
              callbackParams,
              checks,
              extras
            );
          } catch (err) {
            throw createError.BadRequest(err.message);
          }

          let session = Object.assign({}, tokenSet); // Remove non-enumerable methods from the TokenSet

          if (config.afterCallback) {
            session = await config.afterCallback(
              req,
              res,
              session,
              req.openidState
            );
          }

          if (req.oidc.isAuthenticated()) {
            if (req.oidc.user.sub === tokenSet.claims().sub) {
              // If it's the same user logging in again, just update the existing session.
              Object.assign(req[config.session.name], session);
            } else {
              // If it's a different user, replace the session to remove any custom user
              // properties on the session
              replaceSession(req, session, config);
              // And regenerate the session id so the previous user wont know the new user's session id
              regenerateSessionStoreId(req, config);
            }
          } else {
            // If a new user is replacing an anonymous session, update the existing session to keep
            // any anonymous session state (eg. checkout basket)
            Object.assign(req[config.session.name], session);
            // But update the session store id so a previous anonymous user wont know the new user's session id
            regenerateSessionStoreId(req, config);
          }
          attemptSilentLogin.resumeSilentLogin(req, res);

          next();
        } catch (err) {
          // Swallow errors if this is a silentLogin
          if (req.openidState && req.openidState.attemptingSilentLogin) {
            next();
          } else {
            next(err);
          }
        }
      },

--- a/middleware/auth.js
+++ b/middleware/auth.js
@@ -14,7 +14,7 @@
         try {
           const redirectUri = res.oidc.getRedirectUri();

-          let session;
+          let tokenSet;

           try {
             const callbackParams = client.callbackParams(req);
@@ -41,7 +41,7 @@
               extras = { exchangeBody: config.tokenEndpointParams };
             }

-            session = await client.callback(
+            tokenSet = await client.callback(
               redirectUri,
               callbackParams,
               checks,
@@ -51,16 +51,35 @@
             throw createError.BadRequest(err.message);
           }

+          let session = Object.assign({}, tokenSet); // Remove non-enumerable methods from the TokenSet
+
           if (config.afterCallback) {
             session = await config.afterCallback(
               req,
               res,
-              Object.assign({}, session), // Remove non-enumerable methods from the TokenSet
+              session,
               req.openidState
             );
           }

-          Object.assign(req[config.session.name], session);
+          if (req.oidc.isAuthenticated()) {
+            if (req.oidc.user.sub === tokenSet.claims().sub) {
+              // If it's the same user logging in again, just update the existing session.
+              Object.assign(req[config.session.name], session);
+            } else {
+              // If it's a different user, replace the session to remove any custom user
+              // properties on the session
+              replaceSession(req, session, config);
+              // And regenerate the session id so the previous user wont know the new user's session id
+              regenerateSessionStoreId(req, config);
+            }
+          } else {
+            // If a new user is replacing an anonymous session, update the existing session to keep
+            // any anonymous session state (eg. checkout basket)
+            Object.assign(req[config.session.name], session);
+            // But update the session store id so a previous anonymous user wont know the new user's session id
+            regenerateSessionStoreId(req, config);
+          }
           attemptSilentLogin.resumeSilentLogin(req, res);

           next();

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

cd /workspace/express-openid-connect
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
npx mocha --grep "should replace the cookie session when a new user is logging in over an existing different user|should preserve session but regenerate session id when a new user is logging in over an anonymous session|should regenerate the session when a new user is logging in over an existing different user"

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

cd /workspace/express-openid-connect
git apply --whitespace=nowarn /workspace/fix.patch
npx mocha test/callback.tests.js


In the client in Bytom before 1.0.6, checkTopicRegister in p2p/discover/net.go does not prevent negative idx values, leading to a crash.

Integer Overflow or Wraparound

The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.

func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) {
	var pongpkt ingressPacket
	if err := decodePacket(data.Pong, &pongpkt); err != nil {
		return nil, err
	}
	if pongpkt.ev != pongPacket {
		return nil, errors.New("is not pong packet")
	}
	if pongpkt.remoteID != net.tab.self.ID {
		return nil, errors.New("not signed by us")
	}
	// check that we previously authorised all topics
	// that the other side is trying to register.
	hash, _, _ := wireHash(data.Topics)
	if hash != pongpkt.data.(*pong).TopicHash {
		return nil, errors.New("topic hash mismatch")
	}
	if data.Idx < 0 || int(data.Idx) >= len(data.Topics) {
		return nil, errors.New("topic index out of range")
	}
	return pongpkt.data.(*pong), nil
}

func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) {
	var pongpkt ingressPacket
	if err := decodePacket(data.Pong, &pongpkt); err != nil {
		return nil, err
	}
	if pongpkt.ev != pongPacket {
		return nil, errors.New("is not pong packet")
	}
	if pongpkt.remoteID != net.tab.self.ID {
		return nil, errors.New("not signed by us")
	}
	// check that we previously authorised all topics
	// that the other side is trying to register.
	hash, _, _ := wireHash(data.Topics)
	if hash != pongpkt.data.(*pong).TopicHash {
		return nil, errors.New("topic hash mismatch")
	}
	if int(data.Idx) < 0 || int(data.Idx) >= len(data.Topics) {
		return nil, errors.New("topic index out of range")
	}
	return pongpkt.data.(*pong), nil
}

--- a/p2p/discover/net.go
+++ b/p2p/discover/net.go
@@ -15,7 +15,7 @@
 	if hash != pongpkt.data.(*pong).TopicHash {
 		return nil, errors.New("topic hash mismatch")
 	}
-	if data.Idx < 0 || int(data.Idx) >= len(data.Topics) {
+	if int(data.Idx) < 0 || int(data.Idx) >= len(data.Topics) {
 		return nil, errors.New("topic index out of range")
 	}
 	return pongpkt.data.(*pong), nil


This affects all versions of package rollup-plugin-serve. There is no path sanitization in readFile operation.

  const requestListener = (request, response) => {
    // Remove querystring
    const urlPath = decodeURI(request.url.split('?')[0])

    Object.keys(options.headers).forEach((key) => {
      response.setHeader(key, options.headers[key])
    })

    readFileFromContentBase(options.contentBase, urlPath, function (error, content, filePath) {
      if (!error) {
        return found(response, filePath, content)
      }
      if (error.code !== 'ENOENT') {
        response.writeHead(500)
        response.end('500 Internal Server Error' +
          '\n\n' + filePath +
          '\n\n' + Object.values(error).join('\n') +
          '\n\n(rollup-plugin-serve)', 'utf-8')
        return
      }
      if (options.historyApiFallback) {
        var fallbackPath = typeof options.historyApiFallback === 'string' ? options.historyApiFallback : '/index.html'
        readFileFromContentBase(options.contentBase, fallbackPath, function (error, content, filePath) {
          if (error) {
            notFound(response, filePath)
          } else {
            found(response, filePath, content)
          }
        })
      } else {
        notFound(response, filePath)
      }
    })
  }

  // release previous server instance if rollup is reloading configuration in watch mode
  if (server) {

--- a/src/index.js
+++ b/src/index.js
@@ -32,3 +32,6 @@
       }
     })
   }
+
+  // release previous server instance if rollup is reloading configuration in watch mode
+  if (server) {


Grafana is an open-source platform for monitoring and observability. The CSV datasource plugin is a Grafana Labs maintained plugin for Grafana that allows for retrieving and processing CSV data from a remote endpoint configured by an administrator. If this plugin was configured to send requests to a bare host with no path (e.g.  https://www.example.com/ https://www.example.com/` ), requests to an endpoint other than the one configured by the administrator could be triggered by a specially crafted request from any user, resulting in an SSRF vector. AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:N https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator

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 newRequestFromQuery(settings *backend.DataSourceInstanceSettings, customSettings dataSourceSettings, query dataSourceQuery) (*http.Request, error) {
	u, err := url.Parse(settings.URL + query.Path)
	if err != nil {
		return nil, err
	}

	params := make(url.Values)
	for _, p := range query.Params {
		params.Set(p[0], p[1])
	}

	// Query params set by admin overrides params set by query editor.
	values, err := url.ParseQuery(customSettings.QueryParams)
	if err != nil {
		return nil, err
	}
	for k, v := range values {
		params[k] = v
	}

	u.RawQuery = params.Encode()

	var method string
	if query.Method != "" {
		method = query.Method
	} else {
		method = "GET"
	}

	req, err := http.NewRequest(method, u.String(), strings.NewReader(query.Body))
	if err != nil {
		return nil, err
	}

	for _, p := range query.Headers {
		req.Header.Set(p[0], p[1])
	}

	return req, nil
}

func newRequestFromQuery(settings *backend.DataSourceInstanceSettings, customSettings dataSourceSettings, query dataSourceQuery) (*http.Request, error) {
	u, err := url.Parse(settings.URL + query.Path)
	if err != nil {
		return nil, err
	}

	// we need to verify that `query.Path` did not modify the hostname by doing tricks
	settingsURL, err := url.Parse(settings.URL)
	if err != nil {
		return nil, fmt.Errorf("invalid URL")
	}

	if settingsURL.Host != u.Host {
		// the host got changed by adding the path to it. this must not happen.
		return nil, fmt.Errorf("invalid URL + path combination")
	}

	params := make(url.Values)
	for _, p := range query.Params {
		params.Set(p[0], p[1])
	}

	// Query params set by admin overrides params set by query editor.
	values, err := url.ParseQuery(customSettings.QueryParams)
	if err != nil {
		return nil, err
	}
	for k, v := range values {
		params[k] = v
	}

	u.RawQuery = params.Encode()

	var method string
	if query.Method != "" {
		method = query.Method
	} else {
		method = "GET"
	}

	req, err := http.NewRequest(method, u.String(), strings.NewReader(query.Body))
	if err != nil {
		return nil, err
	}

	for _, p := range query.Headers {
		req.Header.Set(p[0], p[1])
	}

	return req, nil
}

--- a/pkg/http_storage.go
+++ b/pkg/http_storage.go
@@ -2,6 +2,17 @@
 	u, err := url.Parse(settings.URL + query.Path)
 	if err != nil {
 		return nil, err
+	}
+
+	// we need to verify that `query.Path` did not modify the hostname by doing tricks
+	settingsURL, err := url.Parse(settings.URL)
+	if err != nil {
+		return nil, fmt.Errorf("invalid URL")
+	}
+
+	if settingsURL.Host != u.Host {
+		// the host got changed by adding the path to it. this must not happen.
+		return nil, fmt.Errorf("invalid URL + path combination")
 	}

 	params := make(url.Values)

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

cd /workspace/grafana-csv-datasource
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
go test  -timeout 30s -run ^TestHTTPStorage_UrlHandling$ github.com/grafana/grafana-csv-datasource/pkg

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

cd /workspace/grafana-csv-datasource
git apply --whitespace=nowarn /workspace/fix.patch
go test -timeout 30s -run '^(TestHTTPStorage_Settings|TestHTTPStorage_Open|TestHTTPStorage_Stat|TestHTTPStorage_Options)$' github.com/grafana/grafana-csv-datasource/pkg


node-jose is a JavaScript implementation of the JSON Object Signing and Encryption (JOSE) for web browsers and node.js-based servers. Prior to version 2.2.0, when using the non-default "fallback" crypto back-end, ECC operations in `node-jose` can trigger a Denial-of-Service (DoS) condition, due to a possible infinite loop in an internal calculation.  For some ECC operations, this condition is triggered randomly; for others, it can be triggered by malicious input. The issue has been patched in version 2.2.0. Since this issue is only present in the "fallback" crypto implementation, it can be avoided by ensuring that either WebCrypto or the Node `crypto` module is available in the JS environment where `node-jose` is being run.

function barrettReduce(x) {
  x.drShiftTo(this.m.t-1,this.r2);
  if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
  this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
  this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
  while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
  x.subTo(this.r2,x);
  while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}

function barrettReduce(x) {
  if (x.s < 0) { throw Error("Barrett reduction on negative input"); }
  x.drShiftTo(this.m.t-1,this.r2);
  if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
  this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
  this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
  while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
  x.subTo(this.r2,x);
  while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}

const originalModInverse = forge.jsbn.BigInteger.prototype.modInverse;
const positiveModInverse = function(m) {
  const inv = originalModInverse.apply(this, [m]);
  return inv.mod(m);
}

forge.jsbn.BigInteger.prototype.modInverse = positiveModInverse;

--- a/lib/deps/ecc/math.js
+++ b/lib/deps/ecc/math.js
@@ -1,4 +1,5 @@
 function barrettReduce(x) {
+  if (x.s < 0) { throw Error("Barrett reduction on negative input"); }
   x.drShiftTo(this.m.t-1,this.r2);
   if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
   this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);

--- /dev/null
+++ b/lib/deps/ecc/math.js
@@ -0,0 +1,7 @@
+const originalModInverse = forge.jsbn.BigInteger.prototype.modInverse;
+const positiveModInverse = function(m) {
+  const inv = originalModInverse.apply(this, [m]);
+  return inv.mod(m);
+}
+
+forge.jsbn.BigInteger.prototype.modInverse = positiveModInverse;


Sails.js before v1.0.0-46 allows attackers to cause a denial of service with a single request because there is no error handler in sails-hook-sockets to handle an empty pathname in a WebSocket request.

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.

    var requestContext = {

      transport: 'socket.io', // TODO: consider if this is really helpful or just a waste of LoC

      protocol: 'ws', // TODO: consider if this is really helpful or just a waste of LoC

      isSocket: true,

      ip      : ip,

      ips     : ips,

      port    : null,

      // Access to underlying SIO socket
      socket  : options.socket,

      url     : options.incomingSailsIOMsg.url,

      path    : url.parse(options.incomingSailsIOMsg.url).pathname,

      method  : options.eventName,

      // Attached data becomes simulated HTTP body (`req.body`)
      // (allow `params` or `data` to be specified for backwards/sideways-compatibility)
      body    : _.isArray(options.incomingSailsIOMsg.data) ? options.incomingSailsIOMsg.data : _.extend({}, options.incomingSailsIOMsg.params || {}, options.incomingSailsIOMsg.data || {}),

      // Allow optional headers
      headers: _.defaults({

        host: app.config.host,

        // Default the "cookie" request header to what was provided in the handshake.
        cookie: (function (){
          var _cookie;
          try {
            _cookie = options.socket.handshake.headers.cookie;
          }
          catch (e) {}
          // console.log('REQUEST to "%s %s" IS USING COOKIE:', options.eventName, options.incomingSailsIOMsg.url, _cookie);
          return _cookie;
        })(),

        nosession: options.socket.handshake.headers.nosession ? true : undefined,

      }, options.incomingSailsIOMsg.headers || {})

    };

    var requestContext = {

      transport: 'socket.io', // TODO: consider if this is really helpful or just a waste of LoC

      protocol: 'ws', // TODO: consider if this is really helpful or just a waste of LoC

      isSocket: true,

      ip      : ip,

      ips     : ips,

      port    : null,

      // Access to underlying SIO socket
      socket  : options.socket,

      url     : options.incomingSailsIOMsg.url,

      path    : url.parse(options.incomingSailsIOMsg.url).pathname || '/',
      // ^^ Uses || '/' because otherwise url.parse returns `null`,
      // which is not a string and thus bad when you try to check
      // .match() of it.

      method  : options.eventName,

      // Attached data becomes simulated HTTP body (`req.body`)
      // (allow `params` or `data` to be specified for backwards/sideways-compatibility)
      body    : _.isArray(options.incomingSailsIOMsg.data) ? options.incomingSailsIOMsg.data : _.extend({}, options.incomingSailsIOMsg.params || {}, options.incomingSailsIOMsg.data || {}),

      // Allow optional headers
      headers: _.defaults({

        host: app.config.host,

        // Default the "cookie" request header to what was provided in the handshake.
        cookie: (function (){
          var _cookie;
          try {
            _cookie = options.socket.handshake.headers.cookie;
          }
          catch (e) {}
          // console.log('REQUEST to "%s %s" IS USING COOKIE:', options.eventName, options.incomingSailsIOMsg.url, _cookie);
          return _cookie;
        })(),

        nosession: options.socket.handshake.headers.nosession ? true : undefined,

      }, options.incomingSailsIOMsg.headers || {})

    };

--- a/lib/receive-incoming-sails-io-msg.js
+++ b/lib/receive-incoming-sails-io-msg.js
@@ -17,7 +17,10 @@

       url     : options.incomingSailsIOMsg.url,

-      path    : url.parse(options.incomingSailsIOMsg.url).pathname,
+      path    : url.parse(options.incomingSailsIOMsg.url).pathname || '/',
+      // ^^ Uses || '/' because otherwise url.parse returns `null`,
+      // which is not a string and thus bad when you try to check
+      // .match() of it.

       method  : options.eventName,


Code Injection in GitHub repository thorsten/phpmyfaq prior to 3.1.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.

document.addEventListener('DOMContentLoaded', () => {
  'use strict';

  $('.btn-edit').on('click', function () {
    const id = $(this).data('btn-id');
    const span = $('span[data-tag-id="' + id + '"]');

    if (span.length > 0) {
      span.replaceWith('<input name="tag" class="form-control" data-tag-id="' + id + '" value="' + span.html() + '">');
    } else {
      const input = $('input[data-tag-id="' + id + '"]');
      input.replaceWith('' + input.val().replace(/\//g, '/') + '');
    }
  });

  $('.tag-form').bind('submit', function (event) {
    event.preventDefault();

    const input = $('input[data-tag-id]:focus');
    const id = input.data('tag-id');
    const tag = input.val();
    const csrf = $('input[name=csrf]').val();

    $.ajax({
      url: 'index.php?action=ajax&ajax=tags&ajaxaction=update',
      type: 'POST',
      data: 'id=' + id + '&tag=' + tag + '&csrf=' + csrf,
      dataType: 'json',
      beforeSend: function () {
        $('#pmf-admin-saving-data-indicator').html(
          'Saving ...'
        );
      },
      success: function (message) {
        input.replaceWith('' + input.val().replace(/\//g, '/') + '');
        $('span[data-tag-id="' + id + '"]');
        $('#pmf-admin-saving-data-indicator').html(message);
      },
    });

    return false;
  });
});

document.addEventListener('DOMContentLoaded', () => {
  'use strict';

  $('.btn-edit').on('click', function () {
    const id = $(this).data('btn-id');
    const span = $('span[data-tag-id="' + id + '"]');

    if (span.length > 0) {
      span.replaceWith('<input name="tag" class="form-control" data-tag-id="' + id + '" value="' + span.html() + '">');
    } else {
      const input = $('input[data-tag-id="' + id + '"]');
      input.replaceWith('' + input.val().replace(/\//g, '/') + '');
    }
  });

  $('.tag-form').bind('submit', function (event) {
    event.preventDefault();

    const input = $('input[data-tag-id]:focus');
    const id = input.data('tag-id');
    const tag = input.val();
    const csrf = $('input[name=csrf]').val();

    const escape = (unsafe) => {
      return unsafe.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''');
    }

    $.ajax({
      url: 'index.php?action=ajax&ajax=tags&ajaxaction=update',
      type: 'POST',
      data: 'id=' + id + '&tag=' + tag + '&csrf=' + csrf,
      dataType: 'json',
      beforeSend: function () {
        $('#pmf-admin-saving-data-indicator').html(
          'Saving ...'
        );
      },
      success: function (message) {
        input.replaceWith('' + escape(input.val().replace(/\//g, '/')) + '');
        $('span[data-tag-id="' + id + '"]');
        $('#pmf-admin-saving-data-indicator').html(message);
      },
    });

    return false;
  });
});

--- a/phpmyfaq/admin/assets/js/tags.js
+++ b/phpmyfaq/admin/assets/js/tags.js
@@ -21,6 +21,10 @@
     const tag = input.val();
     const csrf = $('input[name=csrf]').val();

+    const escape = (unsafe) => {
+      return unsafe.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''');
+    }
+
     $.ajax({
       url: 'index.php?action=ajax&ajax=tags&ajaxaction=update',
       type: 'POST',
@@ -32,7 +36,7 @@
         );
       },
       success: function (message) {
-        input.replaceWith('' + input.val().replace(/\//g, '/') + '');
+        input.replaceWith('' + escape(input.val().replace(/\//g, '/')) + '');
         $('span[data-tag-id="' + id + '"]');
         $('#pmf-admin-saving-data-indicator').html(message);
       },


An improper access control vulnerability (IDOR) exists in the delete attachments functionality of danny-avila/librechat version v0.7.5-rc2. The endpoint does not verify whether the provided attachment ID belongs to the current user, allowing any authenticated user to delete attachments of other users.

router.delete('/', async (req, res) => {
  try {
    const { files: _files } = req.body;

    /** @type {MongoFile[]} */
    const files = _files.filter((file) => {
      if (!file.file_id) {
        return false;
      }
      if (!file.filepath) {
        return false;
      }

      if (/^(file|assistant)-/.test(file.file_id)) {
        return true;
      }

      return isUUID.safeParse(file.file_id).success;
    });

    if (files.length === 0) {
      res.status(204).json({ message: 'Nothing provided to delete' });
      return;
    }

    await processDeleteRequest({ req, files });

    logger.debug(
      `[/files] Files deleted successfully: ${files
        .filter((f) => f.file_id)
        .map((f) => f.file_id)
        .join(', ')}`,
    );
    res.status(200).json({ message: 'Files deleted successfully' });
  } catch (error) {
    logger.error('[/files] Error deleting files:', error);
    res.status(400).json({ message: 'Error in request', error: error.message });
  }
});

router.delete('/', async (req, res) => {
  try {
    const { files: _files } = req.body;

    /** @type {MongoFile[]} */
    const files = _files.filter((file) => {
      if (!file.file_id) {
        return false;
      }
      if (!file.filepath) {
        return false;
      }

      if (/^(file|assistant)-/.test(file.file_id)) {
        return true;
      }

      return isUUID.safeParse(file.file_id).success;
    });

    if (files.length === 0) {
      res.status(204).json({ message: 'Nothing provided to delete' });
      return;
    }

    const fileIds = files.map((file) => file.file_id);
    const userFiles = await getFiles({ file_id: { $in: fileIds }, user: req.user.id });
    if (userFiles.length !== files.length) {
      return res.status(403).json({ message: 'You can only delete your own files' });
    }

    await processDeleteRequest({ req, files: userFiles });

    logger.debug(
      `[/files] Files deleted successfully: ${files
        .filter((f) => f.file_id)
        .map((f) => f.file_id)
        .join(', ')}`,
    );
    res.status(200).json({ message: 'Files deleted successfully' });
  } catch (error) {
    logger.error('[/files] Error deleting files:', error);
    res.status(400).json({ message: 'Error in request', error: error.message });
  }
});

--- a/api/server/routes/files/files.js
+++ b/api/server/routes/files/files.js
@@ -23,7 +23,13 @@
       return;
     }

-    await processDeleteRequest({ req, files });
+    const fileIds = files.map((file) => file.file_id);
+    const userFiles = await getFiles({ file_id: { $in: fileIds }, user: req.user.id });
+    if (userFiles.length !== files.length) {
+      return res.status(403).json({ message: 'You can only delete your own files' });
+    }
+
+    await processDeleteRequest({ req, files: userFiles });

     logger.debug(
       `[/files] Files deleted successfully: ${files


typed-function before 0.10.6 had an arbitrary code execution in the JavaScript engine. Creating a typed function with JavaScript code in the name could result arbitrary execution.

    function _typed(name, signatures) {
      var refs = new Refs();

      // parse signatures, expand them
      var _signatures = parseSignatures(signatures);
      if (_signatures.length == 0) {
        throw new Error('No signatures provided');
      }

      // filter all any type signatures
      var anys = filterAnyTypeSignatures(_signatures);

      // parse signatures into a node tree
      var node = parseTree(_signatures, [], anys);

      //var util = require('util');
      //console.log('ROOT');
      //console.log(util.inspect(node, { depth: null }));

      // generate code for the typed function
      var code = [];
      var _name = name || '';
      var _args = getArgs(maxParams(_signatures));
      code.push('function ' + _name + '(' + _args.join(', ') + ') {');
      code.push('  "use strict";');
      code.push('  var name = \'' + _name + '\';');
      code.push(node.toCode(refs, '  ', false));
      code.push('}');

      // generate body for the factory function
      var body = [
        refs.toCode(),
        'return ' + code.join('\n')
      ].join('\n');

      // evaluate the JavaScript code and attach function references
      var factory = (new Function(refs.name, 'createError', body));
      var fn = factory(refs, createError);

      //console.log('FN\n' + fn.toString()); // TODO: cleanup

      // attach the signatures with sub-functions to the constructed function
      fn.signatures = mapSignatures(_signatures);

      return fn;
    }

    function _typed(name, signatures) {
      var refs = new Refs();

      // parse signatures, expand them
      var _signatures = parseSignatures(signatures);
      if (_signatures.length == 0) {
        throw new Error('No signatures provided');
      }

      // filter all any type signatures
      var anys = filterAnyTypeSignatures(_signatures);

      // parse signatures into a node tree
      var node = parseTree(_signatures, [], anys);

      //var util = require('util');
      //console.log('ROOT');
      //console.log(util.inspect(node, { depth: null }));

      // generate code for the typed function
      // safeName is a conservative replacement of characters
      // to prevend being able to inject JS code at the place of the function name
      // the name is useful for stack trackes therefore we want have it there
      var code = [];
      var safeName = (name || '').replace(/[^a-zA-Z0-9_$]/g, '_')
      var args = getArgs(maxParams(_signatures));
      code.push('function ' + safeName + '(' + args.join(', ') + ') {');
      code.push('  "use strict";');
      code.push('  var name = ' + JSON.stringify(name || '') + ';');
      code.push(node.toCode(refs, '  ', false));
      code.push('}');

      // generate body for the factory function
      var body = [
        refs.toCode(),
        'return ' + code.join('\n')
      ].join('\n');

      // evaluate the JavaScript code and attach function references
      var factory = (new Function(refs.name, 'createError', body));
      var fn = factory(refs, createError);

      //console.log('FN\n' + fn.toString()); // TODO: cleanup

      // attach the signatures with sub-functions to the constructed function
      fn.signatures = mapSignatures(_signatures);

      return fn;
    }

--- a/typed-function.js
+++ b/typed-function.js
@@ -18,12 +18,15 @@
       //console.log(util.inspect(node, { depth: null }));

       // generate code for the typed function
+      // safeName is a conservative replacement of characters
+      // to prevend being able to inject JS code at the place of the function name
+      // the name is useful for stack trackes therefore we want have it there
       var code = [];
-      var _name = name || '';
-      var _args = getArgs(maxParams(_signatures));
-      code.push('function ' + _name + '(' + _args.join(', ') + ') {');
+      var safeName = (name || '').replace(/[^a-zA-Z0-9_$]/g, '_')
+      var args = getArgs(maxParams(_signatures));
+      code.push('function ' + safeName + '(' + args.join(', ') + ') {');
       code.push('  "use strict";');
-      code.push('  var name = \'' + _name + '\';');
+      code.push('  var name = ' + JSON.stringify(name || '') + ';');
       code.push(node.toCode(refs, '  ', false));
       code.push('}');

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

cd /workspace/typed-function
git apply --whitespace=nowarn /workspace/test.patch /workspace/fix.patch
npx mocha --grep "should not allow bad code in the function name" test/security.test.js

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

cd /workspace/typed-function
git apply --whitespace=nowarn /workspace/fix.patch
npx mocha --timeout 3000 test/