CVE-2013-2023: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site scripting (XSS) vulnerability in actionscript/Jplayer.as in the Flash SWF component (jplayer.swf) in jPlayer before 2.3.1 allows remote attackers to inject arbitrary web script or HTML via unspecified vectors, possibly related to incomplete blacklists, a different vulnerability than CVE-2013-1942 and CVE-2013-2022.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Published: 2013-08-15T17:55Z
Repository: https://github.com/happyworm/jPlayer
Language: Other
Files changed: actionscript/Jplayer.as, actionscript/happyworm/jPlayer/JplayerStatus.as, jquery.jplayer/jquery.jplayer.js, package.json
Commit date: 2013-05-14 19:49:26 +0100
Commit message: Security Fix issue #162 for CVE-2013-2023

Diff: diff --git a/actionscript/Jplayer.as b/actionscript/Jplayer.as
index 479bc1d..4451954 100644
--- a/actionscript/Jplayer.as
+++ b/actionscript/Jplayer.as
@@ -6,12 +6,12 @@
  * Dual licensed under the MIT and GPL licenses.
  *  - http://www.opensource.org/licenses/mit-license.php
  *  - http://www.gnu.org/copyleft/gpl.html
  *
  * Author: Mark J Panaghiston
- * Version: 2.3.0
- * Date: 20th April 2013
+ * Version: 2.3.1
+ * Date: 14th May 2013
  *
  * FlashVars expected: (AS3 property of: loaderInfo.parameters)
  *	id: 	(URL Encoded: String) Id of jPlayer instance
  *	vol:	(Number) Sets the initial volume
  *	muted:	(Boolean in a String) Sets the initial muted state
@@ -67,19 +67,18 @@ package {
 		private var isMp4:Boolean = false;
 
 		private var isMp3:Boolean = false;
 		private var isVideo:Boolean = false;
 
-		private var securityIssue:Boolean = false; // When SWF parameters contain illegal characters
-		private var directAccess:Boolean = false; // When SWF visited directly with no parameters (or when security issue detected)
+		private var securityIssue:Boolean = false; // On direct access and when SWF parameters contain illegal characters
 
 		private var txLog:TextField;
 		private var debug:Boolean = false; // Set debug to false for release compile!
 		private var localAIRDebug:Boolean = false; // This is autodetermined by AIR app - leave false!
 
 		private var traceOut:TraceOut;
-		//private var outgoing_lc = new LocalConnection ();
+
 		public function Jplayer() {
 
 			flash.system.Security.allowDomain("*");
 			traceOut = new TraceOut();
 
@@ -88,10 +87,11 @@ package {
 
 			stage.scaleMode = StageScaleMode.NO_SCALE;
 			stage.align = StageAlign.TOP_LEFT;
 
 			if(!securityIssue) {
+				// The jQuery param is the primary cause of security concerns.
 				jQuery = loaderInfo.parameters.jQuery + "('#" + loaderInfo.parameters.id + "').jPlayer";
 				commonStatus.volume = Number(loaderInfo.parameters.vol);
 				commonStatus.muted = loaderInfo.parameters.muted == "true";
 
 				stage.addEventListener(Event.RESIZE, resizeHandler);
@@ -126,30 +126,26 @@ package {
 			menuItem_happyworm.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, menuSelectHandler_happyworm);
 			myContextMenu.customItems.push(menuItem_jPlayer, menuItem_happyworm);
 			contextMenu = myContextMenu;
 
 			// Log console for dev compile option: debug
-			if(debug || directAccess) {
+			if(debug || securityIssue) {
 				txLog = new TextField();
 				txLog.x = 5;
 				txLog.y = 5;
 				txLog.width = stage.stageWidth - 10;
 				txLog.height = stage.stageHeight - 10;
 				txLog.backgroundColor = 0xEEEEFF;
 				txLog.border = true;
 				txLog.background = true;
+				txLog.multiline = true;
 				txLog.text = "jPlayer " + JplayerStatus.VERSION;
 
-				if(debug) {
-					txLog.multiline = true;
-					txLog.visible = false;
-				} else if(directAccess) {
+				if(securityIssue) {
 					txLog.visible = true;
-				}
-				if(debug && directAccess) {
-					txLog.visible = true;
-					log("Direct Access");
+				} else if(debug) {
+					txLog.visible = false;
 				}
 
 				this.addChild(txLog);
 
 				if(debug && !securityIssue) {
@@ -225,27 +221,33 @@ package {
 
 				ExternalInterface.call(jQuery, "jPlayerFlashEvent", JplayerEvent.JPLAYER_READY, extractStatusData(commonStatus)); // See JplayerStatus() class for version number.
 			}
 		}
 		private function checkFlashVars(p:Object):void {
-			var i:Number = 0;
-			for each (var s:String in p) {
-				if(illegalChar(s)) {
-					securityIssue = true; // Illegal char found
+			// Check for direct access. Inspired by mediaelement.js - Also added name to object for non-IE browsers.
+			if(ExternalInterface.objectID != null && ExternalInterface.objectID.toString() != "") {
+				for each (var s:String in p) {
+					if(illegalChar(s) || illegalWord(s)) {
+						securityIssue = true; // Found a security concern.
+					}
 				}
-				i++;
-			}
-			if(i === 0 || securityIssue) {
-				directAccess = true;
+			} else {
+				securityIssue = true; // Direct access disables the callbacks, which were a security concern.
 			}
 		}
 		private function illegalChar(s:String):Boolean {
-			var illegals:String = "' \" ( ) { } * + / \\ < > = document alert";
+			// A whitelist of accepted chars.
+			var validParam:RegExp = /^[-A-Za-z0-9_.]+$/;
+			return !validParam.test(s);
+		}
+		private function illegalWord(s:String):Boolean {
+			// A blacklist of JavaScript commands that are a security concern.
+			var illegals:String = "eval document alert confirm prompt console";
 			if(Boolean(s)) { // Otherwise exception if parameter null.
 				for each (var illegal:String in illegals.split(' ')) {
 					if(s.indexOf(illegal) >= 0) {
-						return true; // Illegal char found
+						return true; // Illegal word found
 					}
 				}
 			}
 			return false;
 		}
@@ -550,11 +552,11 @@ package {
 						mediaY = 0;
 					}
 					resizeEntity(videoItem, mediaX, mediaY, mediaWidth, mediaHeight);
 				}
 			}
-			if((debug || directAccess) && stage.stageWidth > 20 && stage.stageHeight > 20) {
+			if((debug || securityIssue) && stage.stageWidth > 20 && stage.stageHeight > 20) {
 				txLog.width = stage.stageWidth - 10;
 				txLog.height = stage.stageHeight - 10;
 			}
 		}
 		private function resizeEntity(entity:Sprite, mediaX:Number, mediaY:Number, mediaWidth:Number, mediaHeight:Number):void {
diff --git a/actionscript/happyworm/jPlayer/JplayerStatus.as b/actionscript/happyworm/jPlayer/JplayerStatus.as
index 87fd5ea..8a312fb 100644
--- a/actionscript/happyworm/jPlayer/JplayerStatus.as
+++ b/actionscript/happyworm/jPlayer/JplayerStatus.as
@@ -12,11 +12,11 @@
  */
 
 package happyworm.jPlayer {
 	public class JplayerStatus {
 
-		public static const VERSION:String = "2.3.0"; // The version of the Flash jPlayer entity.
+		public static const VERSION:String = "2.3.1"; // The version of the Flash jPlayer entity.
 
 		public var volume:Number = 0.5; // Not affected by reset()
 		public var muted:Boolean = false; // Not affected by reset()
 
 		public var src:String;
diff --git a/jquery.jplayer/Jplayer.swf b/jquery.jplayer/Jplayer.swf
index f324cef..e2c6637 100644
Binary files a/jquery.jplayer/Jplayer.swf and b/jquery.jplayer/Jplayer.swf differ
diff --git a/jquery.jplayer/jquery.jplayer.js b/jquery.jplayer/jquery.jplayer.js
index fea6ba3..cf22a33 100644
--- a/jquery.jplayer/jquery.jplayer.js
+++ b/jquery.jplayer/jquery.jplayer.js
@@ -6,12 +6,12 @@
  * Dual licensed under the MIT and GPL licenses.
  *  - http://www.opensource.org/licenses/mit-license.php
  *  - http://www.gnu.org/copyleft/gpl.html
  *
  * Author: Mark J Panaghiston
- * Version: 2.3.0
- * Date: 20th April 2013
+ * Version: 2.3.1
+ * Date: 14th May 2013
  */
 
 /* Code verified using http://www.jshint.com/ */
 /*jshint asi:false, bitwise:false, boss:false, browser:true, curly:true, debug:false, eqeqeq:true, eqnull:false, evil:false, forin:false, immed:false, jquery:true, laxbreak:false, newcap:true, noarg:true, noempty:true, nonew:true, onevar:false, passfail:false, plusplus:false, regexp:false, undef:true, sub:false, strict:false, white:false, smarttabs:true */
 /*global define:false, ActiveXObject:false, alert:false */
@@ -452,12 +452,12 @@
 	$.jPlayer.keys(true);
 
 	$.jPlayer.prototype = {
 		count: 0, // Static Variable: Change it via prototype.
 		version: { // Static Object
-			script: "2.3.0",
-			needFlash: "2.3.0",
+			script: "2.3.1",
+			needFlash: "2.3.1",
 			flash: "unknown"
 		},
 		options: { // Instanced in $.jPlayer() constructor
 			swfPath: "js", // Path to Jplayer.swf. Can be relative, absolute or server root relative.
 			solution: "html, flash", // Valid solutions: html, flash. Order defines priority. 1st is highest,
@@ -1001,10 +1001,11 @@
 						el.appendChild(p);
 					};
 
 					htmlObj = document.createElement("object");
 					htmlObj.setAttribute("id", this.internal.flash.id);
+					htmlObj.setAttribute("name", this.internal.flash.id);
 					htmlObj.setAttribute("data", this.internal.flash.swf);
 					htmlObj.setAttribute("type", "application/x-shockwave-flash");
 					htmlObj.setAttribute("width", "1"); // Non-zero
 					htmlObj.setAttribute("height", "1"); // Non-zero
 					htmlObj.setAttribute("tabindex", "-1");
diff --git a/package.json b/package.json
index a807529..d059fc4 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "jplayer",
-  "version": "2.3.0",
+  "version": "2.3.1",
   "description": "The jQuery HTML5 Audio / Video Library",
   "homepage": "http://www.jplayer.org/",
   "keywords": [
     "audio",
     "video"

Vulnerable code:  * Version: 2.3.0
 * Date: 20th April 2013
		private var securityIssue:Boolean = false; // When SWF parameters contain illegal characters
		private var directAccess:Boolean = false; // When SWF visited directly with no parameters (or when security issue detected)
		//private var outgoing_lc = new LocalConnection ();
			if(debug || directAccess) {
				if(debug) {
					txLog.multiline = true;
					txLog.visible = false;
				} else if(directAccess) {
				}
				if(debug && directAccess) {
					txLog.visible = true;
					log("Direct Access");
			var i:Number = 0;
			for each (var s:String in p) {
				if(illegalChar(s)) {
					securityIssue = true; // Illegal char found
				i++;
			}
			if(i === 0 || securityIssue) {
				directAccess = true;
			var illegals:String = "' \" ( ) { } * + / \\ < > = document alert";
						return true; // Illegal char found
			if((debug || directAccess) && stage.stageWidth > 20 && stage.stageHeight > 20) {
		public static const VERSION:String = "2.3.0"; // The version of the Flash jPlayer entity.
 * Version: 2.3.0
 * Date: 20th April 2013
			script: "2.3.0",
			needFlash: "2.3.0",
  "version": "2.3.0",
Fixed code:  * Version: 2.3.1
 * Date: 14th May 2013
		private var securityIssue:Boolean = false; // On direct access and when SWF parameters contain illegal characters

				// The jQuery param is the primary cause of security concerns.
			if(debug || securityIssue) {
				txLog.multiline = true;
				if(securityIssue) {
				} else if(debug) {
					txLog.visible = false;
			// Check for direct access. Inspired by mediaelement.js - Also added name to object for non-IE browsers.
			if(ExternalInterface.objectID != null && ExternalInterface.objectID.toString() != "") {
				for each (var s:String in p) {
					if(illegalChar(s) || illegalWord(s)) {
						securityIssue = true; // Found a security concern.
					}
			} else {
				securityIssue = true; // Direct access disables the callbacks, which were a security concern.
			// A whitelist of accepted chars.
			var validParam:RegExp = /^[-A-Za-z0-9_.]+$/;
			return !validParam.test(s);
		}
		private function illegalWord(s:String):Boolean {
			// A blacklist of JavaScript commands that are a security concern.
			var illegals:String = "eval document alert confirm prompt console";
						return true; // Illegal word found
			if((debug || securityIssue) && stage.stageWidth > 20 && stage.stageHeight > 20) {
		public static const VERSION:String = "2.3.1"; // The version of the Flash jPlayer entity.
 * Version: 2.3.1
 * Date: 14th May 2013
			script: "2.3.1",
			needFlash: "2.3.1",
					htmlObj.setAttribute("name", this.internal.flash.id);
  "version": "2.3.1",