{"action":"created","comment":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/comments/60283528","id":60283528,"diff_hunk":"@@ -721,12 +733,197 @@ def test_check_underscore_file_disables_one_violation(self):\n     )\n     def test_check_underscore_no_escape_allowed(self, data):\n         \"\"\"\n-        Test _check_underscore_file_is_safe with expressions that are allowed\n+        Test check_underscore_file_is_safe with expressions that are allowed\n         without escaping because the internal calls properly escape.\n         \"\"\"\n         linter = UnderscoreTemplateLinter()\n         results = FileResults('')\n \n-        linter._check_underscore_file_is_safe(data['template'], results)\n+        linter.check_underscore_file_is_safe(data['template'], results)\n \n         self.assertEqual(len(results.violations), 0)\n+\n+\n+@ddt\n+class TestJavaScriptLinter(TestLinter):\n+    \"\"\"\n+    Test JavaScriptLinter\n+    \"\"\"\n+\n+    @data(\n+        {'template': 'var m = \"Text \" + message + \" text.\"', 'rule': None},\n+        {'template': 'var m = \"<p>\" + message + \"</p>\"', 'rule': Rules.javascript_concat_html},\n+        {'template': 'var m = \" <p> \" + message + \" </p> \"', 'rule': Rules.javascript_concat_html},\n+    )\n+    def test_concat_with_html(self, data):\n+        \"\"\"\n+        Test _check_javascript_file_is_safe with concatenating strings and HTML\n+        \"\"\"\n+        linter = JavaScriptLinter()\n+        results = FileResults('')\n+\n+        linter._check_javascript_file_is_safe(data['template'], results)\n+        self._validate_data_rule(data, results)\n+\n+    @data(\n+        {'template': 'test.append( test.render().el )', 'rule': None},\n+        {'template': 'test.append( test.render().$el )', 'rule': None},\n+        {'template': 'test.append( testEl )', 'rule': None},\n+        {'template': 'test.append( $test )', 'rule': None},\n+        {'template': 'test.append( \"text\" )', 'rule': None},","path":"scripts/tests/test_safe_template_linter.py","position":null,"original_position":181,"commit_id":"51f99f583ebbc3f77af549d3c8c11d8e165470c7","original_commit_id":"889bb9be0a07fc0728a2141d095a7f8dc1316e0d","user":{"login":"robrap","id":14576445,"avatar_url":"https://avatars.githubusercontent.com/u/14576445?v=3","gravatar_id":"","url":"https://api.github.com/users/robrap","html_url":"https://github.com/robrap","followers_url":"https://api.github.com/users/robrap/followers","following_url":"https://api.github.com/users/robrap/following{/other_user}","gists_url":"https://api.github.com/users/robrap/gists{/gist_id}","starred_url":"https://api.github.com/users/robrap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/robrap/subscriptions","organizations_url":"https://api.github.com/users/robrap/orgs","repos_url":"https://api.github.com/users/robrap/repos","events_url":"https://api.github.com/users/robrap/events{/privacy}","received_events_url":"https://api.github.com/users/robrap/received_events","type":"User","site_admin":false},"body":"I added some explanation.  This is an odd case, but it is safe and gets rid of some false positives on other objects that have an append() method and take a string.","created_at":"2016-04-19T18:21:05Z","updated_at":"2016-04-19T18:21:06Z","html_url":"https://github.com/edx/edx-platform/pull/12176#discussion_r60283528","pull_request_url":"https://api.github.com/repos/edx/edx-platform/pulls/12176","_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments/60283528"},"html":{"href":"https://github.com/edx/edx-platform/pull/12176#discussion_r60283528"},"pull_request":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/12176"}}},"pull_request":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/12176","id":66744489,"html_url":"https://github.com/edx/edx-platform/pull/12176","diff_url":"https://github.com/edx/edx-platform/pull/12176.diff","patch_url":"https://github.com/edx/edx-platform/pull/12176.patch","issue_url":"https://api.github.com/repos/edx/edx-platform/issues/12176","number":12176,"state":"open","locked":false,"title":"TNL-4325: Linter Enhancements for JavaScript files","user":{"login":"robrap","id":14576445,"avatar_url":"https://avatars.githubusercontent.com/u/14576445?v=3","gravatar_id":"","url":"https://api.github.com/users/robrap","html_url":"https://github.com/robrap","followers_url":"https://api.github.com/users/robrap/followers","following_url":"https://api.github.com/users/robrap/following{/other_user}","gists_url":"https://api.github.com/users/robrap/gists{/gist_id}","starred_url":"https://api.github.com/users/robrap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/robrap/subscriptions","organizations_url":"https://api.github.com/users/robrap/orgs","repos_url":"https://api.github.com/users/robrap/repos","events_url":"https://api.github.com/users/robrap/events{/privacy}","received_events_url":"https://api.github.com/users/robrap/received_events","type":"User","site_admin":false},"body":"## [TNL-4325](https://openedx.atlassian.net/browse/TNL-4325)\r\n\r\nAdd JavaScript and CoffeeScript rules\r\n\r\n- Check for concatenating strings with HTML.\r\n- Checks various jQuery functions\r\n- Checks CoffeeScript\r\n- Checks underscore templates embedded in JavaScript and CoffeeScript\r\n- Checks interpolate() calls\r\n- Checks escape() calls\r\n\r\nSample output from the latest linter with the new rule:\r\n```\r\ncms/static/js/factories/export.js: 36:20: javascript-concat-html:             msg += '</p><p>' + gettext('The raw error message is:') + '</p>' + errMsg;\r\ncms/static/js/factories/import.js: 33:31: javascript-jquery-html:                 .find('.copy').html(gettext(\"Choose new file\")).end()\r\ncms/static/js/models/settings/course_details.js: 62:57: javascript-interpolate:                 errors.entrance_exam_minimum_score_pct = interpolate(gettext(\"Please enter an integer between %(min)s and %(max)s.\"), range, true);\r\ncms/static/js/views/course_info_update.js: 81:58: javascript-jquery-append:                     $(ele).find('.date-display').parent().append(this.errorTemplate({message : error[field]}));\r\ncms/static/js/views/video/translations_editor.js: 121:24: javascript-jquery-prepend:                     })).prepend(dropdown.clone().val(key))[0];\r\ncms/static/js/views/pages/container.js: 212:73: javascript-jquery-insert-into-target:                     placeholderElement = this.createPlaceholderElement().appendTo(listPanel),\r\ncms/static/js/views/pages/container.js: 322:43: javascript-jquery-insertion:                         this.$el = $(html).replaceAll(element);\r\ncommon/lib/xmodule/xmodule/js/src/problem/edit.coffee: 543:30: javascript-concat-html:           var selectString = '<pre><code>\\n' + p1 + '</code></pre>';\r\ncms/static/js/views/edit_chapter.js: 63:38: javascript-escape:                         {name: course.escape('name')}),\r\n```\r\n\r\n### Testing\r\n- [x] Data is properly encoded in HTML templates to avoid XSS\r\n- [x] Unit, integration, acceptance tests as appropriate\r\n\r\n### Reviewers\r\nIf you've been tagged for review, please check your corresponding box once you've given the :+1:.\r\n- [ ] Code review: @efischer19 \r\n- [ ] Code review: @nedbat \r\n- [ ] Code review: @andy-armstrong \r\n\r\n### Post-review\r\n- [ ] Squash commits","created_at":"2016-04-16T17:36:17Z","updated_at":"2016-04-19T18:21:06Z","closed_at":null,"merged_at":null,"merge_commit_sha":"32e87f292cadeeb6e31f2c53abc72953f9339f9a","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/edx/edx-platform/pulls/12176/commits","review_comments_url":"https://api.github.com/repos/edx/edx-platform/pulls/12176/comments","review_comment_url":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}","comments_url":"https://api.github.com/repos/edx/edx-platform/issues/12176/comments","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/51f99f583ebbc3f77af549d3c8c11d8e165470c7","head":{"label":"edx:robrap/linter-javascript","ref":"robrap/linter-javascript","sha":"51f99f583ebbc3f77af549d3c8c11d8e165470c7","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":10391073,"name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","deployments_url":"https://api.github.com/repos/edx/edx-platform/deployments","created_at":"2013-05-30T20:20:38Z","updated_at":"2016-04-19T15:50:23Z","pushed_at":"2016-04-19T18:19:24Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":754130,"stargazers_count":2510,"watchers_count":2510,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1533,"mirror_url":null,"open_issues_count":184,"forks":1533,"open_issues":184,"watchers":2510,"default_branch":"master"}},"base":{"label":"edx:master","ref":"master","sha":"069e884fdd813a0e394194684709e15df745e5b4","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":10391073,"name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","deployments_url":"https://api.github.com/repos/edx/edx-platform/deployments","created_at":"2013-05-30T20:20:38Z","updated_at":"2016-04-19T15:50:23Z","pushed_at":"2016-04-19T18:19:24Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":754130,"stargazers_count":2510,"watchers_count":2510,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1533,"mirror_url":null,"open_issues_count":184,"forks":1533,"open_issues":184,"watchers":2510,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/12176"},"html":{"href":"https://github.com/edx/edx-platform/pull/12176"},"issue":{"href":"https://api.github.com/repos/edx/edx-platform/issues/12176"},"comments":{"href":"https://api.github.com/repos/edx/edx-platform/issues/12176/comments"},"review_comments":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/12176/comments"},"review_comment":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/12176/commits"},"statuses":{"href":"https://api.github.com/repos/edx/edx-platform/statuses/51f99f583ebbc3f77af549d3c8c11d8e165470c7"}}}}