Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. Argo CD has a Web-based terminal that allows users to get a shell inside a running pod, just as they would with kubectl exec. Starting in version 2.6.0, when the administrator enables this function and grants permission to the user `p, role:myrole, exec, create, */*, allow`, even if the user revokes this permission, the user can still perform operations in the container, as long as the user keeps the terminal view open for a long time. Although the token expiration and revocation of the user are fixed, however, the fix does not address the situation of revocation of only user `p, role:myrole, exec, create, */*, allow` permissions, which may still lead to the leakage of sensitive information. A patch for this vulnerability has been released in Argo CD versions 2.11.7, 2.10.16, and 2.9.21.

func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()

	podName := q.Get("pod")
	container := q.Get("container")
	app := q.Get("appName")
	project := q.Get("projectName")
	namespace := q.Get("namespace")

	if podName == "" || container == "" || app == "" || project == "" || namespace == "" {
		http.Error(w, "Missing required parameters", http.StatusBadRequest)
		return
	}

	appNamespace := q.Get("appNamespace")

	if !argo.IsValidPodName(podName) {
		http.Error(w, "Pod name is not valid", http.StatusBadRequest)
		return
	}
	if !argo.IsValidContainerName(container) {
		http.Error(w, "Container name is not valid", http.StatusBadRequest)
		return
	}
	if !argo.IsValidAppName(app) {
		http.Error(w, "App name is not valid", http.StatusBadRequest)
		return
	}
	if !argo.IsValidProjectName(project) {
		http.Error(w, "Project name is not valid", http.StatusBadRequest)
		return
	}
	if !argo.IsValidNamespaceName(namespace) {
		http.Error(w, "Namespace name is not valid", http.StatusBadRequest)
		return
	}
	if !argo.IsValidNamespaceName(appNamespace) {
		http.Error(w, "App namespace name is not valid", http.StatusBadRequest)
		return
	}

	ns := appNamespace
	if ns == "" {
		ns = s.namespace
	}

	if !security.IsNamespaceEnabled(ns, s.namespace, s.enabledNamespaces) {
		http.Error(w, security.NamespaceNotPermittedError(ns).Error(), http.StatusForbidden)
		return
	}

	shell := q.Get("shell") // No need to validate. Will only be used if it's in the allow-list.

	ctx := r.Context()

	appRBACName := security.RBACName(s.namespace, project, appNamespace, app)
	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACName); err != nil {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, appRBACName); err != nil {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

	fieldLog := log.WithFields(log.Fields{"application": app, "userName": sessionmgr.Username(ctx), "container": container,
		"podName": podName, "namespace": namespace, "project": project, "appNamespace": appNamespace})

	a, err := s.appLister.Applications(ns).Get(app)
	if err != nil {
		if apierr.IsNotFound(err) {
			http.Error(w, "App not found", http.StatusNotFound)
			return
		}
		fieldLog.Errorf("Error when getting app %q when launching a terminal: %s", app, err)
		http.Error(w, "Cannot get app", http.StatusInternalServerError)
		return
	}

	if a.Spec.Project != project {
		fieldLog.Warnf("The wrong project (%q) was specified for the app %q when launching a terminal", project, app)
		http.Error(w, "The wrong project was specified for the app", http.StatusBadRequest)
		return
	}

	config, err := s.getApplicationClusterRawConfig(ctx, a)
	if err != nil {
		http.Error(w, "Cannot get raw cluster config", http.StatusBadRequest)
		return
	}

	kubeClientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		http.Error(w, "Cannot initialize kubeclient", http.StatusBadRequest)
		return
	}

	resourceTree, err := s.appResourceTreeFn(ctx, a)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// From the tree find pods which match the given pod.
	if !podExists(resourceTree.Nodes, podName, namespace) {
		http.Error(w, "Pod doesn't belong to specified app", http.StatusBadRequest)
		return
	}

	pod, err := kubeClientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
	if err != nil {
		fieldLog.Errorf("error retrieving pod: %s", err)
		http.Error(w, "Cannot find pod", http.StatusBadRequest)
		return
	}

	if pod.Status.Phase != v1.PodRunning {
		http.Error(w, "Pod not running", http.StatusBadRequest)
		return
	}

	var findContainer bool
	for _, c := range pod.Spec.Containers {
		if container == c.Name {
			findContainer = true
			break
		}
	}
	if !findContainer {
		fieldLog.Warn("terminal container not found")
		http.Error(w, "Cannot find container", http.StatusBadRequest)
		return
	}

	fieldLog.Info("terminal session starting")

	session, err := newTerminalSession(w, r, nil, s.sessionManager)
	if err != nil {
		http.Error(w, "Failed to start terminal session", http.StatusBadRequest)
		return
	}
	defer session.Done()

	// send pings across the WebSocket channel at regular intervals to keep it alive through
	// load balancers which may close an idle connection after some period of time
	go session.StartKeepalives(time.Second * 5)

	if isValidShell(s.allowedShells, shell) {
		cmd := []string{shell}
		err = startProcess(kubeClientset, config, namespace, podName, container, cmd, session)
	} else {
		// No shell given or the given shell was not allowed: try the configured shells until one succeeds or all fail.
		for _, testShell := range s.allowedShells {
			cmd := []string{testShell}
			if err = startProcess(kubeClientset, config, namespace, podName, container, cmd, session); err == nil {
				break
			}
		}
	}

	if err != nil {
		http.Error(w, "Failed to exec container", http.StatusBadRequest)
		session.Close()
		return
	}

	session.Close()
}

import (
	"encoding/json"
	"fmt"
	"github.com/argoproj/argo-cd/v2/common"
	httputil "github.com/argoproj/argo-cd/v2/util/http"
	util_session "github.com/argoproj/argo-cd/v2/util/session"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	log "github.com/sirupsen/logrus"
	"k8s.io/client-go/tools/remotecommand"
)

type terminalSession struct {
	wsConn         *websocket.Conn
	sizeChan       chan remotecommand.TerminalSize
	doneChan       chan struct{}
	tty            bool
	readLock       sync.Mutex
	writeLock      sync.Mutex
	sessionManager *util_session.SessionManager
	token          *string
}

func newTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager) (*terminalSession, error) {
	token, err := getToken(r)
	if err != nil {
		return nil, err
	}

	conn, err := upgrader.Upgrade(w, r, responseHeader)
	if err != nil {
		return nil, err
	}
	session := &terminalSession{
		wsConn:         conn,
		tty:            true,
		sizeChan:       make(chan remotecommand.TerminalSize),
		doneChan:       make(chan struct{}),
		sessionManager: sessionManager,
		token:          &token,
	}
	return session, nil
}

func (t *terminalSession) Read(p []byte) (int, error) {
	// check if token still valid
	_, newToken, err := t.sessionManager.VerifyToken(*t.token)
	// err in case if token is revoked, newToken in case if refresh happened
	if err != nil || newToken != "" {
		// need to send reconnect code in case if token was refreshed
		return t.reconnect()
	}

	t.readLock.Lock()
	_, message, err := t.wsConn.ReadMessage()
	t.readLock.Unlock()
	if err != nil {
		log.Errorf("read message err: %v", err)
		return copy(p, EndOfTransmission), err
	}
	var msg TerminalMessage
	if err := json.Unmarshal(message, &msg); err != nil {
		log.Errorf("read parse message err: %v", err)
		return copy(p, EndOfTransmission), err
	}
	switch msg.Operation {
	case "stdin":
		return copy(p, msg.Data), nil
	case "resize":
		t.sizeChan <- remotecommand.TerminalSize{Width: msg.Cols, Height: msg.Rows}
		return 0, nil
	default:
		return copy(p, EndOfTransmission), fmt.Errorf("unknown message type %s", msg.Operation)
	}
}

func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()

	podName := q.Get("pod")
	container := q.Get("container")
	app := q.Get("appName")
	project := q.Get("projectName")
	namespace := q.Get("namespace")

	if podName == "" || container == "" || app == "" || project == "" || namespace == "" {
		http.Error(w, "Missing required parameters", http.StatusBadRequest)
		return
	}

	appNamespace := q.Get("appNamespace")

	if !argo.IsValidPodName(podName) {
		http.Error(w, "Pod name is not valid", http.StatusBadRequest)
		return
	}
	if !argo.IsValidContainerName(container) {
		http.Error(w, "Container name is not valid", http.StatusBadRequest)
		return
	}
	if !argo.IsValidAppName(app) {
		http.Error(w, "App name is not valid", http.StatusBadRequest)
		return
	}
	if !argo.IsValidProjectName(project) {
		http.Error(w, "Project name is not valid", http.StatusBadRequest)
		return
	}
	if !argo.IsValidNamespaceName(namespace) {
		http.Error(w, "Namespace name is not valid", http.StatusBadRequest)
		return
	}
	if !argo.IsValidNamespaceName(appNamespace) {
		http.Error(w, "App namespace name is not valid", http.StatusBadRequest)
		return
	}

	ns := appNamespace
	if ns == "" {
		ns = s.namespace
	}

	if !security.IsNamespaceEnabled(ns, s.namespace, s.enabledNamespaces) {
		http.Error(w, security.NamespaceNotPermittedError(ns).Error(), http.StatusForbidden)
		return
	}

	shell := q.Get("shell") // No need to validate. Will only be used if it's in the allow-list.

	ctx := r.Context()

	appRBACName := security.RBACName(s.namespace, project, appNamespace, app)
	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACName); err != nil {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, appRBACName); err != nil {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

	fieldLog := log.WithFields(log.Fields{"application": app, "userName": sessionmgr.Username(ctx), "container": container,
		"podName": podName, "namespace": namespace, "project": project, "appNamespace": appNamespace})

	a, err := s.appLister.Applications(ns).Get(app)
	if err != nil {
		if apierr.IsNotFound(err) {
			http.Error(w, "App not found", http.StatusNotFound)
			return
		}
		fieldLog.Errorf("Error when getting app %q when launching a terminal: %s", app, err)
		http.Error(w, "Cannot get app", http.StatusInternalServerError)
		return
	}

	if a.Spec.Project != project {
		fieldLog.Warnf("The wrong project (%q) was specified for the app %q when launching a terminal", project, app)
		http.Error(w, "The wrong project was specified for the app", http.StatusBadRequest)
		return
	}

	config, err := s.getApplicationClusterRawConfig(ctx, a)
	if err != nil {
		http.Error(w, "Cannot get raw cluster config", http.StatusBadRequest)
		return
	}

	kubeClientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		http.Error(w, "Cannot initialize kubeclient", http.StatusBadRequest)
		return
	}

	resourceTree, err := s.appResourceTreeFn(ctx, a)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// From the tree find pods which match the given pod.
	if !podExists(resourceTree.Nodes, podName, namespace) {
		http.Error(w, "Pod doesn't belong to specified app", http.StatusBadRequest)
		return
	}

	pod, err := kubeClientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
	if err != nil {
		fieldLog.Errorf("error retrieving pod: %s", err)
		http.Error(w, "Cannot find pod", http.StatusBadRequest)
		return
	}

	if pod.Status.Phase != v1.PodRunning {
		http.Error(w, "Pod not running", http.StatusBadRequest)
		return
	}

	var findContainer bool
	for _, c := range pod.Spec.Containers {
		if container == c.Name {
			findContainer = true
			break
		}
	}
	if !findContainer {
		fieldLog.Warn("terminal container not found")
		http.Error(w, "Cannot find container", http.StatusBadRequest)
		return
	}

	fieldLog.Info("terminal session starting")

	session, err := newTerminalSession(ctx, w, r, nil, s.sessionManager, appRBACName, s.enf)
	if err != nil {
		http.Error(w, "Failed to start terminal session", http.StatusBadRequest)
		return
	}
	defer session.Done()

	// send pings across the WebSocket channel at regular intervals to keep it alive through
	// load balancers which may close an idle connection after some period of time
	go session.StartKeepalives(time.Second * 5)

	if isValidShell(s.allowedShells, shell) {
		cmd := []string{shell}
		err = startProcess(kubeClientset, config, namespace, podName, container, cmd, session)
	} else {
		// No shell given or the given shell was not allowed: try the configured shells until one succeeds or all fail.
		for _, testShell := range s.allowedShells {
			cmd := []string{testShell}
			if err = startProcess(kubeClientset, config, namespace, podName, container, cmd, session); err == nil {
				break
			}
		}
	}

	if err != nil {
		http.Error(w, "Failed to exec container", http.StatusBadRequest)
		session.Close()
		return
	}

	session.Close()
}

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
	"github.com/argoproj/argo-cd/v2/util/rbac"

	"github.com/argoproj/argo-cd/v2/common"
	httputil "github.com/argoproj/argo-cd/v2/util/http"
	util_session "github.com/argoproj/argo-cd/v2/util/session"

	"github.com/gorilla/websocket"
	log "github.com/sirupsen/logrus"
	"k8s.io/client-go/tools/remotecommand"
)

type terminalSession struct {
	ctx            context.Context
	wsConn         *websocket.Conn
	sizeChan       chan remotecommand.TerminalSize
	doneChan       chan struct{}
	tty            bool
	readLock       sync.Mutex
	writeLock      sync.Mutex
	sessionManager *util_session.SessionManager
	token          *string
	appRBACName    string
	enf            *rbac.Enforcer
}

func newTerminalSession(ctx context.Context, w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager, appRBACName string, enf *rbac.Enforcer) (*terminalSession, error) {
	token, err := getToken(r)
	if err != nil {
		return nil, err
	}

	conn, err := upgrader.Upgrade(w, r, responseHeader)
	if err != nil {
		return nil, err
	}
	session := &terminalSession{
		ctx:            ctx,
		wsConn:         conn,
		tty:            true,
		sizeChan:       make(chan remotecommand.TerminalSize),
		doneChan:       make(chan struct{}),
		sessionManager: sessionManager,
		token:          &token,
		appRBACName:    appRBACName,
		enf:            enf,
	}
	return session, nil
}

func (t *terminalSession) Read(p []byte) (int, error) {
	// check if token still valid
	_, newToken, err := t.sessionManager.VerifyToken(*t.token)
	// err in case if token is revoked, newToken in case if refresh happened
	if err != nil || newToken != "" {
		// need to send reconnect code in case if token was refreshed
		return t.reconnect()
	}

	// validate permissions
	code, err := t.validatePermissions(p)
	if err != nil {
		return code, err
	}

	t.readLock.Lock()
	_, message, err := t.wsConn.ReadMessage()
	t.readLock.Unlock()
	if err != nil {
		log.Errorf("read message err: %v", err)
		return copy(p, EndOfTransmission), err
	}
	var msg TerminalMessage
	if err := json.Unmarshal(message, &msg); err != nil {
		log.Errorf("read parse message err: %v", err)
		return copy(p, EndOfTransmission), err
	}
	switch msg.Operation {
	case "stdin":
		return copy(p, msg.Data), nil
	case "resize":
		t.sizeChan <- remotecommand.TerminalSize{Width: msg.Cols, Height: msg.Rows}
		return 0, nil
	default:
		return copy(p, EndOfTransmission), fmt.Errorf("unknown message type %s", msg.Operation)
	}
}

func (t *terminalSession) validatePermissions(p []byte) (int, error) {
	permissionDeniedMessage, _ := json.Marshal(TerminalMessage{
		Operation: "stdout",
		Data:      "Permission denied",
	})
	if err := t.enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, t.appRBACName); err != nil {
		err = t.wsConn.WriteMessage(websocket.TextMessage, permissionDeniedMessage)
		if err != nil {
			log.Errorf("permission denied message err: %v", err)
		}
		return copy(p, EndOfTransmission), permissionDeniedErr
	}

	if err := t.enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, t.appRBACName); err != nil {
		err = t.wsConn.WriteMessage(websocket.TextMessage, permissionDeniedMessage)
		if err != nil {
			log.Errorf("permission denied message err: %v", err)
		}
		return copy(p, EndOfTransmission), permissionDeniedErr
	}
	return 0, nil
}

--- a/server/application/terminal.go
+++ b/server/application/terminal.go
@@ -135,7 +135,7 @@

 	fieldLog.Info("terminal session starting")

-	session, err := newTerminalSession(w, r, nil, s.sessionManager)
+	session, err := newTerminalSession(ctx, w, r, nil, s.sessionManager, appRBACName, s.enf)
 	if err != nil {
 		http.Error(w, "Failed to start terminal session", http.StatusBadRequest)
 		return

--- a/server/application/websocket.go
+++ b/server/application/websocket.go
@@ -1,12 +1,17 @@
 import (
+	"context"
 	"encoding/json"
 	"fmt"
+	"net/http"
+	"sync"
+	"time"
+
+	"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
+	"github.com/argoproj/argo-cd/v2/util/rbac"
+
 	"github.com/argoproj/argo-cd/v2/common"
 	httputil "github.com/argoproj/argo-cd/v2/util/http"
 	util_session "github.com/argoproj/argo-cd/v2/util/session"
-	"net/http"
-	"sync"
-	"time"

 	"github.com/gorilla/websocket"
 	log "github.com/sirupsen/logrus"

--- a/server/application/websocket.go
+++ b/server/application/websocket.go
@@ -1,4 +1,5 @@
 type terminalSession struct {
+	ctx            context.Context
 	wsConn         *websocket.Conn
 	sizeChan       chan remotecommand.TerminalSize
 	doneChan       chan struct{}
@@ -7,4 +8,6 @@
 	writeLock      sync.Mutex
 	sessionManager *util_session.SessionManager
 	token          *string
+	appRBACName    string
+	enf            *rbac.Enforcer
 }

--- a/server/application/websocket.go
+++ b/server/application/websocket.go
@@ -1,4 +1,4 @@
-func newTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager) (*terminalSession, error) {
+func newTerminalSession(ctx context.Context, w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager, appRBACName string, enf *rbac.Enforcer) (*terminalSession, error) {
 	token, err := getToken(r)
 	if err != nil {
 		return nil, err
@@ -9,12 +9,15 @@
 		return nil, err
 	}
 	session := &terminalSession{
+		ctx:            ctx,
 		wsConn:         conn,
 		tty:            true,
 		sizeChan:       make(chan remotecommand.TerminalSize),
 		doneChan:       make(chan struct{}),
 		sessionManager: sessionManager,
 		token:          &token,
+		appRBACName:    appRBACName,
+		enf:            enf,
 	}
 	return session, nil
 }

--- a/server/application/websocket.go
+++ b/server/application/websocket.go
@@ -5,6 +5,12 @@
 	if err != nil || newToken != "" {
 		// need to send reconnect code in case if token was refreshed
 		return t.reconnect()
+	}
+
+	// validate permissions
+	code, err := t.validatePermissions(p)
+	if err != nil {
+		return code, err
 	}

 	t.readLock.Lock()

--- /dev/null
+++ b/server/application/websocket.go
@@ -0,0 +1,22 @@
+func (t *terminalSession) validatePermissions(p []byte) (int, error) {
+	permissionDeniedMessage, _ := json.Marshal(TerminalMessage{
+		Operation: "stdout",
+		Data:      "Permission denied",
+	})
+	if err := t.enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, t.appRBACName); err != nil {
+		err = t.wsConn.WriteMessage(websocket.TextMessage, permissionDeniedMessage)
+		if err != nil {
+			log.Errorf("permission denied message err: %v", err)
+		}
+		return copy(p, EndOfTransmission), permissionDeniedErr
+	}
+
+	if err := t.enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, t.appRBACName); err != nil {
+		err = t.wsConn.WriteMessage(websocket.TextMessage, permissionDeniedMessage)
+		if err != nil {
+			log.Errorf("permission denied message err: %v", err)
+		}
+		return copy(p, EndOfTransmission), permissionDeniedErr
+	}
+	return 0, nil
+}


In irisnet-crypto before 1.1.7 for IRISnet, the util/utils.js file allows code execution because of unsafe eval usage.

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.

    static sortObjectKeys(obj) {
        let sort = function (obj) {
            let tmp = {};
            Object.keys(obj).sort().forEach(function (k) {
                if (Array.isArray(obj[k])) {
                    let p = [];
                    obj[k].forEach(function (item) {
                        if (item != null && typeof(item) === "object") {
                            p.push(sort(item));
                        } else {
                            p.push(item);
                        }
                    });
                    tmp[k] = p;
                } else if (obj[k] != null && typeof(obj[k]) === "object") {
                    tmp[k] = sort(obj[k]);
                } else if (obj[k] != null && typeof(obj[k]) === "function") {
                    tmp[k] = eval(obj[k].toString())
                } else {
                    tmp[k] = new String(obj[k]).toString();
                }
            });
            return tmp;
        };
        return sort(obj)
    }

    static sortObjectKeys(obj) {
        let sort = function (obj) {
            let tmp = {};
            Object.keys(obj).sort().forEach(function (k) {
                if (Array.isArray(obj[k])) {
                    let p = [];
                    obj[k].forEach(function (item) {
                        if (item != null && typeof(item) === "object") {
                            p.push(sort(item));
                        } else {
                            p.push(item);
                        }
                    });
                    tmp[k] = p;
                } else if (obj[k] != null && typeof(obj[k]) === "object") {
                    tmp[k] = sort(obj[k]);
                } else if (obj[k] != null && typeof(obj[k]) === "function") {
                    tmp[k] = evil(obj[k].toString())
                } else {
                    tmp[k] = new String(obj[k]).toString();
                }
            });
            return tmp;
        };
        return sort(obj)
    }

function evil(fn) {
    let Fn = Function;
    return new Fn('return ' + fn)();
}

--- a/util/utils.js
+++ b/util/utils.js
@@ -15,7 +15,7 @@
                 } else if (obj[k] != null && typeof(obj[k]) === "object") {
                     tmp[k] = sort(obj[k]);
                 } else if (obj[k] != null && typeof(obj[k]) === "function") {
-                    tmp[k] = eval(obj[k].toString())
+                    tmp[k] = evil(obj[k].toString())
                 } else {
                     tmp[k] = new String(obj[k]).toString();
                 }

--- a/util/utils.js
+++ b/util/utils.js
@@ -1 +1,4 @@
-};
+function evil(fn) {
+    let Fn = Function;
+    return new Fn('return ' + fn)();
+}


Sqlalchemy mako before 1.2.2 is vulnerable to Regular expression Denial of Service when using the Lexer class to parse. This also affects babelplugin and linguaplugin.

Inefficient Regular Expression Complexity

The product uses a regular expression with an inefficient, possibly exponential worst-case computational complexity that consumes excessive CPU cycles.

    def match_tag_start(self):
        match = self.match(
            r"""
            \<%     # opening tag

            ([\w\.\:]+)   # keyword

            ((?:\s+\w+|\s*=\s*|".*?"|'.*?')*)  # attrname, = \
                                               #        sign, string expression

            \s*     # more whitespace

            (/)?>   # closing

            """,
            re.I | re.S | re.X,
        )

        if not match:
            return False

        keyword, attr, isend = match.groups()
        self.keyword = keyword
        attributes = {}
        if attr:
            for att in re.findall(
                r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr
            ):
                key, val1, val2 = att
                text = val1 or val2
                text = text.replace("\r\n", "\n")
                attributes[key] = text
        self.append_node(parsetree.Tag, keyword, attributes)
        if isend:
            self.tag.pop()
        elif keyword == "text":
            match = self.match(r"(.*?)(?=\</%text>)", re.S)
            if not match:
                raise exceptions.SyntaxException(
                    "Unclosed tag: <%%%s>" % self.tag[-1].keyword,
                    **self.exception_kwargs,
                )
            self.append_node(parsetree.Text, match.group(1))
            return self.match_tag_end()
        return True

    def match_tag_start(self):
        reg = r"""
            \<%     # opening tag

            ([\w\.\:]+)   # keyword

            ((?:\s+\w+|\s*=\s*|"[^"]*?"|'[^']*?'|\s*,\s*)*)  # attrname, = \
                                               #        sign, string expression
                                               # comma is for backwards compat
                                               # identified in #366

            \s*     # more whitespace

            (/)?>   # closing

        """

        match = self.match(
            reg,
            re.I | re.S | re.X,
        )

        if not match:
            return False

        keyword, attr, isend = match.groups()
        self.keyword = keyword
        attributes = {}
        if attr:
            for att in re.findall(
                r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr
            ):
                key, val1, val2 = att
                text = val1 or val2
                text = text.replace("\r\n", "\n")
                attributes[key] = text
        self.append_node(parsetree.Tag, keyword, attributes)
        if isend:
            self.tag.pop()
        elif keyword == "text":
            match = self.match(r"(.*?)(?=\</%text>)", re.S)
            if not match:
                raise exceptions.SyntaxException(
                    "Unclosed tag: <%%%s>" % self.tag[-1].keyword,
                    **self.exception_kwargs,
                )
            self.append_node(parsetree.Text, match.group(1))
            return self.match_tag_end()
        return True

--- a/mako/lexer.py
+++ b/mako/lexer.py
@@ -1,18 +1,22 @@
     def match_tag_start(self):
-        match = self.match(
-            r"""
+        reg = r"""
             \<%     # opening tag

             ([\w\.\:]+)   # keyword

-            ((?:\s+\w+|\s*=\s*|".*?"|'.*?')*)  # attrname, = \
+            ((?:\s+\w+|\s*=\s*|"[^"]*?"|'[^']*?'|\s*,\s*)*)  # attrname, = \
                                                #        sign, string expression
+                                               # comma is for backwards compat
+                                               # identified in #366

             \s*     # more whitespace

             (/)?>   # closing

-            """,
+        """
+
+        match = self.match(
+            reg,
             re.I | re.S | re.X,
         )