{"action":"created","comment":{"url":"https://api.github.com/repos/edx/edx-documentation/pulls/comments/55521625","id":55521625,"diff_hunk":"@@ -0,0 +1,731 @@\n+.. _Safe Templates:\n+\n+######################################\n+Safe Templates\n+######################################\n+\n+Cross Site Scripting (XSS) vulnerabilities allow user-supplied data to be\n+incorrectly executed as code in a web browser. Writing templates that are safe\n+from XSS security vulnerabilities can be difficult. This section presents best\n+practices for handling proper escaping in the Open edX platform to avoid these\n+vulnerabilities.\n+\n+.. note:: If you become aware of security issues, do not report them in\n+   public. Instead, please email security@edx.org.\n+\n+.. contents::\n+   :depth: 1\n+   :local:\n+\n+\n+Philosophy and General Rules\n+****************************\n+\n+The philosophy behind the recommendations in this section is to make things as\n+simple as possible for developers. Protecting against XSS typically requires\n+properly escaping user-provided data before it is placed on the page. Rather\n+than trying to determine if data is user-provided and could be compromised, we\n+will play it safe and escape everything. Unfortunately, since there are many\n+different rules for escaping, you still must choose the proper type of\n+escaping.\n+\n+Here are some general rules.\n+\n+#. **Escape always.** Assume that all data is untrusted and escape it appropriately.\n+   Do not try to determine whether data could or could not be manipulated by a\n+   user.\n+\n+#. **Escape late.** Delay escaping as long as possible, until you can see the\n+   actual context and understand the proper escaping that is required for\n+   the context. Browsers interpret different contexts such as HTML, URLs,\n+   CSS, and Javascript/JSON with different rules, so there are different\n+   escaping requirements based on where the data is being used in a page.\n+\n+#. **Escape appropriately.** Know what kind of data you have (HTML, plain text,\n+   JSON, etc.) and where it is going (HTML, JavaScript, etc.). Choose the\n+   proper escaping method based on these details.\n+\n+#. **Validation is not sufficient.** Validating inputs does not replace the\n+   need to properly escape. In some cases, this may reduce the likelihood of\n+   potential problems, but proper escaping is always necessary.\n+\n+#. **Do not store escaped data.** Again, because you do not know ahead of time\n+   all the places that the data will be used, you must wait until you have\n+   the proper context to decide on the proper escaping.\n+\n+\n+Types of Context and Escaping\n+*****************************\n+\n+The following diagram provides a high-level overview of the relationship\n+between the different templates, different contexts of DOM creation and\n+manipulation, and different types of escaping. As a general rule, proper\n+escaping is related to the context in which the data is being written, and\n+might not match the context that will eventually be reading the data.\n+\n+.. image:: images/safe-templates.png\n+    :width: 666px\n+    :height: 289px\n+    :align: center\n+    :alt: A diagram detailing how data flows from Django applications and Django models through Mako templates and Django templates into an HTML page. Data can then flow from the HTML page into JavaScript, and back down into the DOM through jQuery or Underscore.js templates.\n+\n+In the Open edX platform, data flows from the application to the initial HTML page\n+mainly through the use of Mako templates.\n+\n+.. Make sure the numbers in the list below are in sync with the numbered arrows in\n+.. the safe-templates.png diagram above, if either the diagram or the list is modified.\n+\n+The numbered arrows are detailed below.\n+\n+#. This step represents the use of Mako templates to write general HTML tags\n+   (that is, tags other than ``<script>`` tags) to create the HTML page. Any\n+   data written to the page inside one of these HTML tags or HTML attributes\n+   must be HTML-escaped to be treated as plain text.\n+\n+#. This step represents the use of Mako templates to write JavaScript inside\n+   a ``<script>`` tag in the HTML page. Data written to this context must all\n+   be JavaScript-escaped to keep it from mistakenly being treated as HTML.\n+   Note that this data should not be HTML-escaped, which must happen in a\n+   later step if it is written back to the DOM by JavaScript.\n+\n+#. This step represents the loading and executing of JavaScript from the HTML\n+   page by the JavaScript engine. This step should be safe, if data was\n+   properly JavaScript-escaped earlier.\n+\n+#. In this step, JavaScript might load additional data from HTML tags and\n+   attributes using the DOM. Sometimes data is passed in this way via data\n+   attributes. This data is typically read using jQuery functions, but it can\n+   be read using other JavaScript functions. The data can be in the form of\n+   plain text strings or JSON. If it is in the form of strings, this data\n+   should mostly be plain text. Be careful with the escaping in this case.\n+   Although the data is used in JavaScript, it is transmitted as HTML, and so\n+   must be HTML-escaped.\n+\n+#. In this step, JavaScript is being used to edit the DOM, often by creating\n+   HTML tags or setting HTML attributes. Often this is done using jQuery\n+   functions. Since HTML tags and attributes are being written here, any plain\n+   text must be properly HTML-escaped.\n+\n+#. This step represents a subset of DOM manipulation using JavaScript,\n+   specifically through use of Underscore.js templates. Although these\n+   templates have a specific syntax for escaping, because HTML tags and\n+   attributes are being written any plain text must be properly HTML-escaped.\n+\n+\n+How Escaping Makes Templates Safe\n+*********************************\n+\n+.. highlight:: mako\n+\n+Let's review a simple example of an XSS attack and how proper escaping might\n+prevent such an attack. Imagine that we find the following expression in a\n+Mako template.\n+\n+.. code-block:: mako\n+\n+    <div>${course_name}</div>\n+\n+Imagine further that someone uses Studio to set the course name as shown in\n+this example, including the HTML ``<script>`` tag.\n+\n+.. code-block:: mako\n+\n+    <script>alert('XSS attack!');</script>\n+\n+The following resulting unsafe page source is sent to the browser.\n+\n+.. code-block:: mako\n+\n+    <div><script>alert('XSS attack!');</script></div>\n+\n+The browser would execute the JavaScript code in the ``<script>alert('XSS\n+attack!');</script>`` tag. The user has injected code into the page that would\n+display a pop-up alert, which we would not want to allow. Because this attack\n+could contain arbitrary JavaScript that would be executed by the browser with\n+the same trust as any JavaScript that is sent from the application, it has the\n+potential to do something much more malicious than simply displaying a pop-up.\n+An example might be to steal and email the user's cookies.\n+\n+In Mako, you can introduce HTML-escaping for all expressions on a page using\n+the page directive with the ``h`` filter.  Here is an example of an expression\n+that is properly HTML-escaped.\n+\n+.. code-block:: mako\n+\n+    <%page expression_filter=\"h\"/>\n+    ...\n+    <div>${course_name}</div>\n+\n+The resulting safe page source is as follows.\n+\n+.. code-block:: mako\n+\n+    <div>&lt;script&gt;alert(&#39;XSS!&#39;);&lt;/script&gt;</div>\n+\n+This time, the browser will not interpret the ``<script>`` tag as a JavaScript\n+context, and instead simply displays the original string in the page.\n+\n+Now, let's review an example of an expression used in a valid JavaScript context\n+created using a ``<script>`` tag inside a Mako template.\n+\n+.. code-block:: mako\n+\n+    <script type=\"text/javascript\">\n+       var courseName = \"${course_name}\";\n+       ...\n+    </script>\n+\n+For this example, imagine that someone set the course name as shown here.\n+\n+.. code-block:: mako\n+\n+    \";alert('XSS attack!');\"\n+\n+The resulting unsafe page source, sent to the browser with no escaping, would look\n+like this.\n+\n+.. code-block:: mako\n+\n+    <script type=\"text/javascript\">\n+       var courseName = \"\";alert('XSS attack!');\"\";\n+       ...\n+    </script>\n+\n+You can see how the attacker closed out the string and again tricked the browser\n+into executing the malicious JavaScript in the context of JavaScript. There\n+are several reasons why you do not want to use the default HTML-escaping here.\n+\n+#. JavaScript-escaping will also escape all characters that are special\n+   characters in HTML, such as  ``<``. However, JavaScript-escaping will\n+   escape ``<`` to ``\\u003C``, rather than to ``&lt;``. This will still keep\n+   the browser from finding an HTML tag where it does not belong.\n+\n+#. The resultant string might not ultimately be used in an HTML context, so\n+   HTML entities might not be the proper escaping.\n+\n+The way to properly JavaScript-escape code in Mako is shown in the following\n+example.\n+\n+.. code-block:: mako\n+\n+    <%! from openedx.core.djangolib.js_utils import js_escaped_string %>\n+    ...\n+    <script type=\"text/javascript\">\n+       var courseName = \"${course_name | n, js_escaped_string}\";\n+       ...\n+    </script>\n+\n+The code above would produce the following safe page source.\n+\n+.. code-block:: mako\n+\n+    <script type=\"text/javascript\">\n+       var courseName = \"\\u0022\\u003Balert(\\u0027XSS attack!\\u0027)\\u003B\\u0022\\u0022\\u003B\";\n+       ...\n+    </script>\n+\n+\n+Strategy for Safe by Default\n+****************************\n+\n+.. highlight:: mako\n+\n+Our templates will be safe only when developers understand why and how to\n+properly handle escaping in the many different languages and contexts.\n+\n+Currently, by default, our Mako templates perform no escaping for expressions.\n+We refer to this as not being \"safe by default\". Our intention is get to the\n+state where our Mako templates *are* \"safe by default\", by ensuring that Mako\n+template expressions perform HTML-escaping by default. It is important to\n+understand that HTML-escaping might not always be the right thing to do, but\n+it is a good starting place. We have provided additional escaping filters to\n+help with other scenarios.\n+\n+Due to valid exceptions to the general rule of HTML-escaping, it is not yet\n+possible to configure escaping for all Mako templates in the entire platform\n+without introducing errors. Therefore, the strategy is to make this transition\n+template by template by introducing the following line to each template.\n+\n+.. code-block:: mako\n+\n+    <%page expression_filter=\"h\"/>\n+\n+At some point in the future, we will be able to enable HTML-escaping for the\n+entire platform, and we will be able to remove these lines.\n+\n+Soon we will also have a linter available that will help with the transition\n+to Mako templates that are safe by default. Additionally, the tool will help\n+us maintain safer JavaScript and Underscore.js template files, which do not\n+have a way to set a default escaping mechanism.\n+\n+\n+Editing Template Files\n+**********************\n+\n+When you edit template files (including Mako templates, Underscore templates,\n+or JavaScript), use the appropriate conventions.\n+\n+The sections that follow address these points for each type of file.\n+\n+#. What has to be at the top of the file (if anything) to make it safe?\n+\n+#. How is code properly escaped? The answer is different depending on the\n+   templating language and the context.\n+\n+#. How do you properly handle internationalization and escaping together? For\n+   more information, see :ref:`i18n`.\n+\n+.. note:: Remember to take into account not just the programming language\n+   involved, but the type of file. For example, JavaScript embedded in an HTML\n+   Mako template is treated differently than JavaScript in a pure .js file.\n+\n+.. contents::\n+   :depth: 1\n+   :local:\n+\n+.. _Safe Django Template Files:\n+\n+Django Template Files\n+=====================\n+\n+.. highlight:: django\n+\n+Django templates are considered \"safe by default\", meaning that expressions\n+are HTML-escaped by default. HTML-escaping is not always the right choice for\n+escaping, for example, with embedded JavaScript.\n+\n+Additional documentation is required to handle the exceptional cases for Django\n+templates, including internationalization and embedded JavaScript.\n+\n+.. _Safe Mako Template Calls:\n+\n+Mako Template() Calls\n+=====================\n+\n+.. highlight:: mako\n+\n+If a Mako template is loaded from Python outside of the general template loading\n+scheme, the following default filters should be provided to make the template\n+safe by default (i.e. use HTML-escaping by default).\n+\n+.. code-block:: mako\n+\n+    template = Template(\" ... \",\n+        default_filters=['decode.utf8', 'h'],\n+    )\n+\n+\n+.. _Safe Mako Template Files:\n+\n+Mako Template Files\n+===================\n+\n+.. highlight:: mako\n+\n+For Mako templates, all expressions will use HTML-escaping by default.  This is\n+accomplished by adding the following directive to the very top of each\n+template. ::\n+\n+    <%page expression_filter=\"h\"/>\n+\n+Using this default HTML-escaping, the following combination will represent an\n+HTML-escaped expression. ::\n+\n+    <%page expression_filter=\"h\"/>\n+    ...\n+    ${data}\n+\n+If you need to disable the default filters, you must use the ``n`` filter as\n+the first filter. This can be seen in some of the examples below.\n+\n+For more information, see\n+`Mako: Expression Filtering <http://docs.makotemplates.org/en/latest/filtering.html>`_.\n+\n+There are two filters, ``dump_html_escaped_json`` and ``dump_js_escaped_json``,\n+which can be used to convert a Python object to JSON. It is important that you\n+use the proper filter based on the context to which you are writing the data.\n+\n+When you need to dump JSON in the context of HTML (for example, into a data\n+attribute), you must use ``dump_html_escaped_json``. This same filter can be\n+used for numbers and booleans in addition to dicts and lists. If you have a\n+string, continue to use the default ``h`` filter.\n+\n+.. code-block:: mako\n+\n+    <%page expression_filter=\"h\"/>\n+    <%! from openedx.core.djangolib.js_utils import dump_html_escaped_json %>\n+    ...\n+    <div\n+        data-course-name='${course.name}'\n+        data-course-options='${course.options | n, dump_html_escaped_json}'\n+        data-course-max-students='${course.max_students | n, dump_html_escaped_json}'\n+        data-course-is-great='${course.is_great | n, dump_html_escaped_json}'\n+    ></div>\n+\n+On the other hand, if you are in a JavaScript context, you must use either the\n+``js_escaped_string`` or ``dump_js_escaped_json`` filters. These are the\n+JavaScript-escaping equivalents of ``h`` and ``dump_html_escaped_json``\n+respectively.\n+\n+With ``js_escaped_string`` you must supply the enclosing quotes. When ``None``\n+is supplied to ``js_escaped_string``, it results in an empty string for\n+convenience.\n+\n+The JavaScript context may either appear explicitly through the use of a\n+``<script>`` tag, or implicitly through the use of ``<%static:require_module>``,\n+which itself sets up the ``<script>`` context.\n+\n+Here is an example of ``js_escaped_string`` and ``dump_js_escaped_json`` in the\n+context of JavaScript in a Mako template.\n+\n+.. code-block:: mako\n+\n+    <%namespace name='static' file='static_content.html'/>\n+    <%!\n+    from openedx.core.djangolib.js_utils import (\n+        dump_js_escaped_json, js_escaped_string\n+    )\n+    %>\n+    ...\n+    <%static:require_module module_name=\"js/course_factory\" class_name=\"CourseFactory\">\n+        CourseFactory({\n+            course_name: '${course.name | n, js_escaped_string}',\n+            course_options: ${course.options | n, dump_js_escaped_json},\n+            course_max_students: ${course.max_students | n, dump_js_escaped_json},\n+            course_is_great: ${course.is_great | n, dump_js_escaped_json},\n+        });\n+    </%static:require_module>\n+\n+If you have a string that already contains JSON and might contain user\n+provided data, the only way to make sure it is safe is to use ``json.loads``\n+and then use either ``dump_js_escaped_json`` or ``dump_html_escaped_json``,\n+depending on the context. In some cases, this issue can be resolved by\n+removing a call to ``json.dumps`` from a Python file, and instead passing the\n+Python object as-is to the Mako template, where it will be properly converted\n+to a string using these filters.\n+\n+There are also special methods useful for properly escaping and translating\n+strings. In order to mix plain text and HTML using ``format()``, you must use\n+the ``HTML()`` and ``Text()`` functions. Use the ``HTML()`` function when you\n+have a replacement string that contains HTML tags. In order for the ``HTML()``\n+function to work, you must first use the ``Text()`` function to wrap the plain\n+text translated string. Both the ``HTML()`` and ``Text()`` functions must be\n+closed before any calls to ``format()``.\n+\n+.. code-block:: mako\n+\n+    <%page expression_filter=\"h\"/>\n+    <%!\n+    from django.utils.translation import ugettext as _\n+\n+    from openedx.core.djangolib.markup import Text, HTML\n+    %>\n+    ...\n+    ${Text(_(\"Click over to {link_start}the home page{link_end}.\")).format(\n+        link_start=HTML('<a href=\"/home\">'),\n+        link_end=HTML('</a>'),\n+    )}\n+\n+For more details on translating strings and proper escaping, see :ref:`i18n`.\n+\n+There are some rare cases where we need to turn off default HTML-escaping using","path":"en_us/developers/source/safe_templates.rst","position":431,"original_position":431,"commit_id":"e493c3d05cd04ec67f8803fa5727a8d1c569810b","original_commit_id":"e493c3d05cd04ec67f8803fa5727a8d1c569810b","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":"@e0d The following sentences and example are supposed to provide examples. \r\nHere is an alternative intro sentence that scopes this to a \"specific expression\":\r\nIn some rare cases we need to turn off default HTML-escaping using ``| n, unicode`` for a specific expression.","created_at":"2016-03-09T14:06:15Z","updated_at":"2016-03-09T14:06:15Z","html_url":"https://github.com/edx/edx-documentation/pull/892#discussion_r55521625","pull_request_url":"https://api.github.com/repos/edx/edx-documentation/pulls/892","_links":{"self":{"href":"https://api.github.com/repos/edx/edx-documentation/pulls/comments/55521625"},"html":{"href":"https://github.com/edx/edx-documentation/pull/892#discussion_r55521625"},"pull_request":{"href":"https://api.github.com/repos/edx/edx-documentation/pulls/892"}}},"pull_request":{"url":"https://api.github.com/repos/edx/edx-documentation/pulls/892","id":60692756,"html_url":"https://github.com/edx/edx-documentation/pull/892","diff_url":"https://github.com/edx/edx-documentation/pull/892.diff","patch_url":"https://github.com/edx/edx-documentation/pull/892.patch","issue_url":"https://api.github.com/repos/edx/edx-documentation/issues/892","number":892,"state":"open","locked":false,"title":"Add safe templates documentation","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":"## [DOC-2739](https://openedx.atlassian.net/browse/DOC-2739)\r\n\r\n### Description\r\n\r\n- Add safe template document to read the docs\r\n- Update i18n doc with new best practices\r\nRelates to [TNL-4160](https://openedx.atlassian.net/browse/TNL-4160)\r\n\r\n### Date Needed\r\n\r\nThis will be used for a coding bug bash in mid-March.\r\n\r\n### Reviewers\r\nPR submitter checks the boxes after each reviewer finishes and gives :+1:. \r\n- [ ] Subject matter expert: @nedbat \r\n- [ ] Subject matter expert: @e0d \r\n- [ ] Doc team review (sanity check/copy edit/dev edit): @catong \r\n\r\nFYI: @scottrish \r\n\r\n### Testing\r\n- [X] Ran ./run_tests.sh without warnings or errors\r\n \r\n### Post-review\r\n- [ ] Squash commits","created_at":"2016-02-25T15:09:26Z","updated_at":"2016-03-09T14:06:15Z","closed_at":null,"merged_at":null,"merge_commit_sha":"462f15ccf2ad9a8ed8fc401ab16fa76851542680","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/edx/edx-documentation/pulls/892/commits","review_comments_url":"https://api.github.com/repos/edx/edx-documentation/pulls/892/comments","review_comment_url":"https://api.github.com/repos/edx/edx-documentation/pulls/comments{/number}","comments_url":"https://api.github.com/repos/edx/edx-documentation/issues/892/comments","statuses_url":"https://api.github.com/repos/edx/edx-documentation/statuses/e493c3d05cd04ec67f8803fa5727a8d1c569810b","head":{"label":"edx:robrap/safe-templates","ref":"robrap/safe-templates","sha":"e493c3d05cd04ec67f8803fa5727a8d1c569810b","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":23839256,"name":"edx-documentation","full_name":"edx/edx-documentation","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-documentation","description":"For published versions of the edX documentation, visit:","fork":false,"url":"https://api.github.com/repos/edx/edx-documentation","forks_url":"https://api.github.com/repos/edx/edx-documentation/forks","keys_url":"https://api.github.com/repos/edx/edx-documentation/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-documentation/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-documentation/teams","hooks_url":"https://api.github.com/repos/edx/edx-documentation/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-documentation/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-documentation/events","assignees_url":"https://api.github.com/repos/edx/edx-documentation/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-documentation/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-documentation/tags","blobs_url":"https://api.github.com/repos/edx/edx-documentation/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-documentation/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-documentation/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-documentation/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-documentation/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-documentation/languages","stargazers_url":"https://api.github.com/repos/edx/edx-documentation/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-documentation/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-documentation/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-documentation/subscription","commits_url":"https://api.github.com/repos/edx/edx-documentation/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-documentation/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-documentation/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-documentation/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-documentation/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-documentation/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-documentation/merges","archive_url":"https://api.github.com/repos/edx/edx-documentation/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-documentation/downloads","issues_url":"https://api.github.com/repos/edx/edx-documentation/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-documentation/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-documentation/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-documentation/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-documentation/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-documentation/releases{/id}","deployments_url":"https://api.github.com/repos/edx/edx-documentation/deployments","created_at":"2014-09-09T15:29:04Z","updated_at":"2016-02-23T06:10:59Z","pushed_at":"2016-03-09T03:42:41Z","git_url":"git://github.com/edx/edx-documentation.git","ssh_url":"git@github.com:edx/edx-documentation.git","clone_url":"https://github.com/edx/edx-documentation.git","svn_url":"https://github.com/edx/edx-documentation","homepage":" http://docs.edx.org","size":95175,"stargazers_count":33,"watchers_count":33,"language":"Makefile","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":122,"mirror_url":null,"open_issues_count":20,"forks":122,"open_issues":20,"watchers":33,"default_branch":"master"}},"base":{"label":"edx:master","ref":"master","sha":"db5700460b8146ef75a2ae69e3fd1e945b9573c2","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":23839256,"name":"edx-documentation","full_name":"edx/edx-documentation","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-documentation","description":"For published versions of the edX documentation, visit:","fork":false,"url":"https://api.github.com/repos/edx/edx-documentation","forks_url":"https://api.github.com/repos/edx/edx-documentation/forks","keys_url":"https://api.github.com/repos/edx/edx-documentation/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-documentation/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-documentation/teams","hooks_url":"https://api.github.com/repos/edx/edx-documentation/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-documentation/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-documentation/events","assignees_url":"https://api.github.com/repos/edx/edx-documentation/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-documentation/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-documentation/tags","blobs_url":"https://api.github.com/repos/edx/edx-documentation/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-documentation/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-documentation/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-documentation/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-documentation/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-documentation/languages","stargazers_url":"https://api.github.com/repos/edx/edx-documentation/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-documentation/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-documentation/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-documentation/subscription","commits_url":"https://api.github.com/repos/edx/edx-documentation/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-documentation/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-documentation/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-documentation/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-documentation/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-documentation/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-documentation/merges","archive_url":"https://api.github.com/repos/edx/edx-documentation/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-documentation/downloads","issues_url":"https://api.github.com/repos/edx/edx-documentation/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-documentation/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-documentation/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-documentation/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-documentation/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-documentation/releases{/id}","deployments_url":"https://api.github.com/repos/edx/edx-documentation/deployments","created_at":"2014-09-09T15:29:04Z","updated_at":"2016-02-23T06:10:59Z","pushed_at":"2016-03-09T03:42:41Z","git_url":"git://github.com/edx/edx-documentation.git","ssh_url":"git@github.com:edx/edx-documentation.git","clone_url":"https://github.com/edx/edx-documentation.git","svn_url":"https://github.com/edx/edx-documentation","homepage":" http://docs.edx.org","size":95175,"stargazers_count":33,"watchers_count":33,"language":"Makefile","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":122,"mirror_url":null,"open_issues_count":20,"forks":122,"open_issues":20,"watchers":33,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/edx/edx-documentation/pulls/892"},"html":{"href":"https://github.com/edx/edx-documentation/pull/892"},"issue":{"href":"https://api.github.com/repos/edx/edx-documentation/issues/892"},"comments":{"href":"https://api.github.com/repos/edx/edx-documentation/issues/892/comments"},"review_comments":{"href":"https://api.github.com/repos/edx/edx-documentation/pulls/892/comments"},"review_comment":{"href":"https://api.github.com/repos/edx/edx-documentation/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/edx/edx-documentation/pulls/892/commits"},"statuses":{"href":"https://api.github.com/repos/edx/edx-documentation/statuses/e493c3d05cd04ec67f8803fa5727a8d1c569810b"}}}}