예제 #1
0
def test_disallow_unknown_css():
    with pytest.raises(TypeError):
        sanitize_html("""
            <div class='test'>
                <span style='position: absolute; top: 0;'>Text</span>
            </div>
        """)
예제 #2
0
def test_disallow_onhover():
    with pytest.raises(naucse.sanitize.DisallowedAttribute):
        sanitize_html("""
            <div class="test" onhover="alert('XSS')">
                <em">Text</em>
            </div>
        """)
예제 #3
0
def test_bad_style():
    with pytest.raises(naucse.sanitize.DisallowedStyle):
        sanitize_html("""
            <style>
            .green {
                color: green
            </style>
        """)
예제 #4
0
def test_wrong_elements():
    with pytest.raises(naucse.sanitize.DisallowedStyle):
        sanitize_html("""
            <style>
            .green {
                color: green;
            }
            </style>
        """)
예제 #5
0
def test_invalid_multiple_css_selectors():
    with pytest.raises(naucse.sanitize.DisallowedStyle):
        sanitize_html("""
            <style>
            .dataframe .green, .also-green {
                color: green;
            }
            </style>
        """)
예제 #6
0
def lesson(lesson, page, solution=None):
    """Render the html of the given lesson page."""

    lesson_url, subpage_url, static_url = relative_url_functions(
        request.path, None, lesson)

    page = lesson.pages[page]

    content = page_content(lesson,
                           page,
                           solution=solution,
                           lesson_url=lesson_url,
                           subpage_url=subpage_url,
                           static_url=static_url)

    content = content["content"]
    content = sanitize_html(content)

    kwargs = {}
    if solution is not None:
        kwargs["solution_number"] = int(solution)

    return render_template("lesson.html",
                           content=content,
                           page=page,
                           lesson=lesson,
                           edit_info=get_edit_info(page.edit_path),
                           title=page.title,
                           **kwargs)
예제 #7
0
def course(course):
    if course.is_link():
        naucse.utils.views.forks_raise_if_disabled()

        try:
            data_from_fork = course.render_course(request_url=request.path)
            edit_info = links.process_edit_info(
                data_from_fork.get("edit_info"))
            content = data_from_fork.get("content")
            if content is None:
                raise InvalidInfo("Content of the page can't be None.")
        except POSSIBLE_FORK_EXCEPTIONS as e:
            if raise_errors_from_forks():
                raise

            # there's no way to replace this page, render an error page instead
            logger.error("There was an error rendering url %s for course '%s'",
                         request.path, course.slug)
            logger.exception(e)
            return render_template(
                "error_in_fork.html",
                malfunctioning_course=course,
                edit_info=get_edit_info(course.edit_path),
                faulty_page="course",
                root_slug=model.meta.slug,
                travis_build_id=os.environ.get("TRAVIS_BUILD_ID"),
            )
        record_content_urls(data_from_fork, f"/{course.slug}/")
        kwargs = {
            "course_content": content,
            "edit_info": edit_info,
        }
    else:
        content = course_content(course)
        content = sanitize_html(content)

        kwargs = {
            "course_content": content,
            "edit_info": get_edit_info(course.edit_path),
        }

    recent_runs = get_recent_runs(course)

    try:
        return render_template("course.html",
                               course=course,
                               title=course.title,
                               recent_runs=recent_runs,
                               **kwargs)
    except TemplateNotFound:
        abort(404)
예제 #8
0
def course_calendar(course):
    if course.is_link():
        naucse.utils.views.forks_raise_if_disabled()

        try:
            data_from_fork = course.render_calendar(request_url=request.path)
            record_content_urls(data_from_fork, f"/{course.slug}/")

            course = process_course_data(data_from_fork.get("course"),
                                         slug=course.slug)
            edit_info = links.process_edit_info(
                data_from_fork.get("edit_info"))
            content = data_from_fork.get("content")

            if content is None:
                raise InvalidInfo("Content of the page can't be None.")
        except POSSIBLE_FORK_EXCEPTIONS as e:
            if raise_errors_from_forks():
                raise

            logger.error("There was an error rendering url %s for course '%s'",
                         request.path, course.slug)
            logger.exception(e)
            return render_template(
                "error_in_fork.html",
                malfunctioning_course=course,
                edit_info=get_edit_info(course.edit_path),
                faulty_page="calendar",
                root_slug=model.meta.slug,
                travis_build_id=os.environ.get("TRAVIS_BUILD_ID"),
            )

        kwargs = {"course": course, "edit_info": edit_info, "content": content}
    else:
        if not course.start_date:
            abort(404)

        content = course_calendar_content(course)
        content = sanitize_html(content)

        kwargs = {
            "course": course,
            "edit_info": get_edit_info(course.edit_path),
            "content": content
        }

    return render_template('course_calendar.html', **kwargs)
예제 #9
0
    def convert(self, content):
        """Sanitize HTML and rewrites URLs for given content."""
        def page_url(*, lesson, page='index', **kw):
            return self.page.course.get_lesson_url(lesson, page=page)

        def solution_url(*, solution, **kw):
            return self.page.solutions[int(solution)].get_url(**kw)

        def static_url(*, filename, **kw):
            return self.page.lesson.static_files[filename].get_url(**kw)

        return sanitize.sanitize_html(content,
                                      naucse_urls={
                                          'page': page_url,
                                          'solution': solution_url,
                                          'static': static_url,
                                      })
예제 #10
0
def _sanitize_page_content(parent, content):
    """Sanitize HTML for a particular page. Also rewrites URLs."""
    parent_page = getattr(parent, 'page', parent)

    def page_url(*, lesson, page='index', **kw):
        return parent_page.course.get_lesson_url(lesson, page=page)

    def solution_url(*, solution, **kw):
        return parent_page.solutions[int(solution)].get_url(**kw)

    def static_url(*, filename, **kw):
        return parent_page.lesson.static_files[filename].get_url(**kw)

    return sanitize.sanitize_html(content,
                                  naucse_urls={
                                      'page': page_url,
                                      'solution': solution_url,
                                      'static': static_url,
                                  })
예제 #11
0
    def render(self, page_type, *args, **kwargs):
        """Render a page in the fork.

        Check the content and registers URLs to freeze.
        """
        naucse.utils.views.forks_raise_if_disabled()

        task = Task(
            "naucse.utils.forks:render",
            args=[page_type, self.slug] + list(args),
            kwargs=kwargs,
        )
        result = arca.run(self.repo,
                          self.branch,
                          task,
                          reference=Path("."),
                          depth=None)

        if page_type != "calendar_ics" and result.output["content"] is not None:
            result.output["content"] = sanitize_html(result.output["content"])

        return result.output
예제 #12
0
파일: models.py 프로젝트: hroncok/naucse
 def load(self, value, context, *, parent):
     if self.sanitizer is None:
         return sanitize.sanitize_html(value)
     return self.sanitizer(parent, value)
예제 #13
0
 def load(self, value, context, *, parent):
     return sanitize.sanitize_html(value)
예제 #14
0
def assert_changed(input_html, expected):
    input_html = dedent(input_html).strip()
    expected = dedent(expected).strip()
    output_html = sanitize_html(input_html)
    assert output_html == expected
예제 #15
0
def test_disallow_javascript_href():
    with pytest.raises(naucse.sanitize.DisallowedURLScheme):
        sanitize_html(
            """<div class='test'><img src="javascript:alert('XSS')" /></div>"""
        )
예제 #16
0
def course_page(course, lesson, page, solution=None):
    lesson_slug = lesson
    page_slug = page

    try:
        lesson = model.get_lesson(lesson_slug)
        canonical_url = url_for('lesson', lesson=lesson, _external=True)
    except LookupError:
        lesson = canonical_url = None

    kwargs = {}
    prev_link = session_link = next_link = session = None

    if course.is_link():
        naucse.utils.views.forks_raise_if_disabled()

        fork_kwargs = {"request_url": request.path}

        try:
            # Checks if the rendered page content is in cache locally
            # to offer it to the fork.
            # ``course.vars`` calls ``course_info`` so it has to be in
            # the try block.
            # The function can also raise FileNotFoundError if the
            # lesson doesn't exist in repo.
            content_key = page_content_cache_key(
                arca.get_repo(course.repo, course.branch), lesson_slug, page,
                solution, course.vars)
            content_offer = arca.region.get(content_key)

            # We've got the fragment in cache, let's offer it to the fork.
            if content_offer:
                fork_kwargs["content_key"] = content_key

            data_from_fork = course.render_page(lesson_slug, page, solution,
                                                **fork_kwargs)
            record_content_urls(data_from_fork, f"/{course.slug}/")

            content = data_from_fork["content"]

            if content is None:
                # the offer was accepted
                content = content_offer["content"]
                for x in content_offer["urls"]:
                    record_url(urljoin(request.path, x))
            else:
                # the offer was rejected or the the fragment was not in cache
                arca.region.set(content_key, {
                    "content": content,
                    "urls": data_from_fork["content_urls"]
                })
                for x in data_from_fork["content_urls"]:
                    record_url(urljoin(request.path, x))

            # compatibility
            page = process_page_data(data_from_fork.get("page"))
            course = process_course_data(data_from_fork.get("course"),
                                         slug=course.slug)
            session = process_session_data(data_from_fork.get("session"))
            kwargs["edit_info"] = links.process_edit_info(
                data_from_fork.get("edit_info"))
            prev_link, session_link, next_link = process_footer_data(
                data_from_fork.get("footer"))

            title = '{}: {}'.format(course["title"], page["title"])
        except POSSIBLE_FORK_EXCEPTIONS as e:
            if raise_errors_from_forks():
                raise

            rendered_replacement = False

            logger.error("There was an error rendering url %s for course '%s'",
                         request.path, course.slug)
            if lesson is not None:
                try:
                    logger.error(
                        "Rendering the canonical version with a warning.")

                    lesson_url, subpage_url, static_url = relative_url_functions(
                        request.path, course, lesson)
                    page = lesson.pages[page]
                    content = page_content(lesson,
                                           page,
                                           solution,
                                           course,
                                           lesson_url=lesson_url,
                                           subpage_url=subpage_url,
                                           static_url=static_url)["content"]
                    title = '{}: {}'.format(course.title, page.title)

                    try:
                        footer_links = course.get_footer_links(
                            lesson.slug,
                            page_slug,
                            request_url=request.path,
                        )
                        for link in footer_links:
                            _prefix = f"/{course.slug}/"
                            if link and link["url"].startswith(_prefix):
                                record_url(link["url"])
                        prev_link, session_link, next_link = footer_links

                    except POSSIBLE_FORK_EXCEPTIONS as e:
                        if raise_errors_from_forks():
                            raise

                        # The fork is failing spectacularly, so the footer
                        # links aren't that important
                        logger.error(
                            "Could not retrieve even footer links from the fork at page %s",
                            request.path)
                        logger.exception(e)

                    rendered_replacement = True
                    kwargs["edit_info"] = get_edit_info(page.edit_path)
                    kwargs["error_in_fork"] = True
                    kwargs["travis_build_id"] = os.environ.get(
                        "TRAVIS_BUILD_ID")

                except Exception as canonical_error:
                    logger.error("Rendering the canonical version failed.")
                    logger.exception(canonical_error)

            if not rendered_replacement:
                logger.exception(e)
                return render_template(
                    "error_in_fork.html",
                    malfunctioning_course=course,
                    edit_info=get_edit_info(course.edit_path),
                    faulty_page="lesson",
                    lesson=lesson_slug,
                    pg=page_slug,  # avoid name conflict
                    solution=solution,
                    root_slug=model.meta.slug,
                    travis_build_id=os.environ.get("TRAVIS_BUILD_ID"),
                )
    else:
        if lesson is None:
            abort(404)

        lesson_url, subpage_url, static_url = relative_url_functions(
            request.path, course, lesson)
        page, session, prv, nxt = get_page(course, lesson, page)
        prev_link, session_link, next_link = get_footer_links(
            course, session, prv, nxt, lesson_url)

        content = page_content(lesson,
                               page,
                               solution,
                               course=course,
                               lesson_url=lesson_url,
                               subpage_url=subpage_url,
                               static_url=static_url)
        content = content["content"]
        content = sanitize_html(content)
        title = '{}: {}'.format(course.title, page.title)

        kwargs["edit_info"] = get_edit_info(page.edit_path)

    if solution is not None:
        kwargs["solution_number"] = int(solution)

    return render_template("lesson.html",
                           canonical_url=canonical_url,
                           title=title,
                           content=content,
                           prev_link=prev_link,
                           session_link=session_link,
                           next_link=next_link,
                           root_slug=model.meta.slug,
                           course=course,
                           lesson=lesson,
                           page=page,
                           solution=solution,
                           session=session,
                           **kwargs)
예제 #17
0
def session_coverpage(course, session, coverpage):
    """Render the session coverpage.

    Args:
        course      course where the session belongs
        session     name of the session
        coverpage   coverpage of the session, front is default

    Returns:
        rendered session coverpage
    """
    if course.is_link():
        naucse.utils.views.forks_raise_if_disabled()

        try:
            data_from_fork = course.render_session_coverpage(
                session, coverpage, request_url=request.path)
            record_content_urls(data_from_fork, f"/{course.slug}/")

            content = data_from_fork.get("content")
            if content is None:
                raise InvalidInfo("Content of the page can't be None.")

            kwargs = {
                "course":
                process_course_data(data_from_fork.get("course"),
                                    slug=course.slug),
                "session":
                process_session_data(data_from_fork.get("session"),
                                     slug=session),
                "edit_info":
                links.process_edit_info(data_from_fork.get("edit_info")),
                "content":
                content
            }
        except POSSIBLE_FORK_EXCEPTIONS as e:
            if raise_errors_from_forks():
                raise

            # there's no way to replace this page, render an error page instead
            logger.error("There was an error rendering url %s for course '%s'",
                         request.path, course.slug)
            logger.exception(e)
            return render_template(
                "error_in_fork.html",
                malfunctioning_course=course,
                edit_info=get_edit_info(course.edit_path),
                faulty_page=f"session_{coverpage}",
                session=session,
                root_slug=model.meta.slug,
                travis_build_id=os.environ.get("TRAVIS_BUILD_ID"),
            )
    else:
        session = course.sessions.get(session)

        content = session_coverpage_content(course, session, coverpage)
        content = sanitize_html(content)

        kwargs = {
            "course": course,
            "session": session,
            "edit_info":
            get_edit_info(session.get_edit_path(course, coverpage)),
            "content": content
        }

    return render_template("coverpage.html", **kwargs)
예제 #18
0
def test_disallow_relative_url():
    with pytest.raises(naucse.sanitize.DisallowedLink):
        sanitize_html("""
            <a href="/courses">Text</a>
        """)
예제 #19
0
def test_disallow_script():
    with pytest.raises(naucse.sanitize.DisallowedElement):
        sanitize_html("<div><script>alert('XSS')</script></div>")