Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.848
diff -u -p -r1.848 common.inc
--- includes/common.inc	14 Jan 2009 21:13:41 -0000	1.848
+++ includes/common.inc	16 Jan 2009 23:49:08 -0000
@@ -1821,7 +1821,11 @@ function l($text, $path, array $options 
     $options['attributes']['title'] = strip_tags($options['attributes']['title']);
   }
 
-  return '<a href="' . check_url(url($path, $options)) . '"' . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text : check_plain($text)) . '</a>';
+  // If the link is external, ensure it's valid, for internal links this will
+  // be checked internally by url().
+  $url = isset($options['external']) ? check_url(url($path, $options)) : url($path, $options);
+
+  return '<a href="' . $url . '"' . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text : check_plain($text)) . '</a>';
 }
 
 /**
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.21
diff -u -p -r1.21 common.test
--- modules/simpletest/tests/common.test	11 Jan 2009 08:39:08 -0000	1.21
+++ modules/simpletest/tests/common.test	16 Jan 2009 23:49:14 -0000
@@ -1,6 +1,50 @@
 <?php
 // $Id: common.test,v 1.21 2009/01/11 08:39:08 dries Exp $
 
+class CommonLUnitTest extends DrupalWebTestCase {
+
+  function getInfo() {
+    return array(
+      'name' => t('Tests for the l() function'),
+      'description' => t('Confirm that url() works correctly with various input.'),
+      'group' => t('System'),
+    );
+  }
+
+  function testL() {
+
+    // Pass in some basic XSS attack vectors and confirm the strings are
+    // escaped correctly.
+    $text = $this->randomName();
+    $path = 'javascript:alert("test");';
+    $options = array('external' => TRUE);
+    $link = l($text, $path);
+    $this->assertTrue(strpos($link, $path) === FALSE);
+
+    // Add $options['external'] this time.
+    $link = l($text, $path, $options);
+    $this->assertTrue(strpos($link, $path) === FALSE, t('Invalid path was filtered by l()'));
+
+    // Try a more sneaky XSS attack from http://ha.ckers.org/xss.html
+    $path = '\'\';!--"<XSS>=&{()}';
+    $link = l($text, $path);
+    $this->assertTrue(strpos($link, '<XSS') === FALSE, t('Invalid path was filtered by l().'));
+
+    $link = l($text, $path, $options);
+    $this->assertTrue(strpos($link, '<XSS') === FALSE, t('Invalid path was filtred by l()'));
+
+    $path = '<IMG """><SCRIPT>alert("XSS")</SCRIPT>">';
+    $link = l($text, $path);
+    $this->assertTrue(strpos($link, 'alert("XSS")') === FALSE, t('Invalid path was filtered by l()'));
+
+    $path = '<IMG """><SCRIPT>alert("XSS")</SCRIPT>">';
+    $link = l($text, $path, $options);
+    $this->assertTrue(strpos($link, 'alert("XSS")') === FALSE, t('Invalid path was filtered by l()'));
+  }
+}
+
+
+
 class CommonSizeTestCase extends DrupalWebTestCase {
   protected $exact_test_cases;
   protected $rounded_test_cases;
@@ -700,4 +744,4 @@ class DrupalErrorCollectionUnitTest exte
       $this->assertEqual($error['message'], $message, t("Message was %message", array('%message' => $message)));
     }
   }
-}
\ No newline at end of file
+}
