diff --git a/cve/django/2022/CVE-2022-22818/README.md b/cve/django/2022/CVE-2022-22818/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..cd0364664e5a07e2a96e214120942c0df851c1f2
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/README.md
@@ -0,0 +1,38 @@
+# Run tests on django
+
+```bash
+python runfuzz.py
+```
+
+### To run django server:
+
+```bash
+python manage.py runserver
+```
+
+### Check the python modules are installed:
+
+```bash
+python3 -m django --version # pip install django==4.0.1
+grammarinator-generate --version # pip install grammarinator
+pip install selenium # should be installed
+pip install webdriver-manager # also
+pip install progress
+```
+
+### Prepare fuzzer and check that all is ok
+
+```bash
+touch grammars/fuzzer/HTMLGenerator.py # Otherwise it will not find the file
+grammarinator-process grammars/HTMLLexer.g4 grammars/HTMLParser.g4 -o grammars/fuzzer # Fill the file
+```
+
+Try fuzzer:
+
+```bash
+grammarinator-generate grammars.fuzzer.HTMLCustomGenerator.HTMLCustomGenerator -r htmlDocument -d 20 -o grammars/examples/test_%d.html -n 10 --sys-path ./
+cd grammars/examples/
+ls # Here is your files.
+python -m http.server # start the server
+```
+
diff --git a/cve/django/2022/CVE-2022-22818/djangocontext.py b/cve/django/2022/CVE-2022-22818/djangocontext.py
new file mode 100644
index 0000000000000000000000000000000000000000..14a5495a08b73f9905989da5d4e1f3b77ce82f07
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/djangocontext.py
@@ -0,0 +1,100 @@
+import json
+from logging import Logger
+import logging
+import os
+from typing import List
+from mylogger import setup_logger
+
+class CommentSection:
+ inner: str
+ type: str
+ name: str
+
+ def __init__(self, section_data: str, info_json_payload: str):
+ self.inner = section_data
+ info = json.loads(info_json_payload)
+ self.type = info['type']
+ self.name = info['name']
+
+ def isBlock(self) -> bool:
+ return self.type == "block"
+
+ def isInclude(self) -> bool:
+ return self.type == "include"
+
+class ContextLoader:
+ logger: Logger = logging.getLogger("ContextLoader")
+ ctx_json_data: str
+ text: str
+ template_relative_path: str
+ base_file_relative_path: str
+ created_included_files: List[str]
+
+ COMMENT = "{% comment %}"
+ ENDCOMMENT = "{% endcomment %}"
+ END_INFO = "#}"
+
+ def __init__(self, template_filename: str):
+ self.base_file_relative_path = None
+ self.created_included_files = []
+ if template_filename is not None:
+ self.template_relative_path = template_filename
+ with open(template_filename, "r") as text_file:
+ self.ctx_json_data = text_file.readline().strip().removeprefix("{#").removesuffix("#}")
+ self.text = text_file.read()
+ else:
+ self.logger.error("No file passed!")
+
+ def get_context(self) -> dict:
+ return json.loads(self.ctx_json_data)
+
+ def get_sections(self) -> List[CommentSection]:
+ res = []
+ found_pos = self.text.find(self.COMMENT)
+ while found_pos != -1:
+ info_start_pos = found_pos + len(self.COMMENT)
+ info_end_pos = self.text.find(self.END_INFO, info_start_pos)
+ section_start_index = info_end_pos + len(self.END_INFO)
+ info_json = self.text[info_start_pos:section_start_index].strip().removeprefix("{#").removesuffix("#}")
+ section_end_index = self.text.find(self.ENDCOMMENT, section_start_index)
+ section_data = self.text[section_start_index:section_end_index]
+ cmt = CommentSection(section_data, info_json)
+ res.append(cmt)
+ found_pos = self.text.find(self.COMMENT, section_end_index + len(self.ENDCOMMENT))
+ return res
+
+ def create_and_modify_files_if_need(self):
+ sections = self.get_sections()
+ if any(map(lambda x: x.isBlock(), sections)):
+ self.logger.info("Modifying file %s as it contains blocks overriding.", self.template_relative_path)
+ filename_without_extension = self.template_relative_path.removesuffix(".html")
+ self.base_file_relative_path = filename_without_extension + "_base.html"
+ os.rename(self.template_relative_path, self.base_file_relative_path)
+ with open(self.template_relative_path, "w") as text_file:
+ base_filename = os.path.basename(self.base_file_relative_path)
+ text_file.write("{% extends \"./" + base_filename + "\" %}\n\n")
+ for section in filter(lambda x: x.isBlock(), sections):
+ self.logger.debug("Writing block '%s'.", section.name)
+ text_file.write("{% block " + section.name + " %}\n")
+ text_file.write(os.linesep.join([s for s in section.inner.splitlines() if s]) + "\n")
+ text_file.write("{% endblock %}\n\n\n")
+ if any(map(lambda x: x.isInclude(), sections)):
+ self.logger.info("Creating files for %s as it contains include keywords.", self.template_relative_path)
+ dirpath_to_create_files = os.path.dirname(os.path.abspath(self.template_relative_path))
+ self.logger.info("Will create files in %s directory.", dirpath_to_create_files)
+ for section in filter(lambda x: x.isInclude(), sections):
+ new_filepath = os.path.join(dirpath_to_create_files, section.name)
+ with open(new_filepath, "w") as text_file:
+ self.logger.debug("Writing file '%s'.", section.name)
+ text_file.write(os.linesep.join([s for s in section.inner.splitlines() if s]) + "\n")
+ self.created_included_files.append(new_filepath)
+ self.logger.info("Saved for future deletion %s.", new_filepath)
+
+ def remove_created_files(self):
+ if self.base_file_relative_path is not None and os.path.exists(self.base_file_relative_path):
+ self.logger.info("Deleting base file %s...", self.base_file_relative_path)
+ os.remove(self.base_file_relative_path)
+ for filename in self.created_included_files:
+ if os.path.exists(filename):
+ self.logger.info("Deleting included file %s...", filename)
+ os.remove(filename)
\ No newline at end of file
diff --git a/cve/django/2022/CVE-2022-22818/grammars/HTMLLexer.g4 b/cve/django/2022/CVE-2022-22818/grammars/HTMLLexer.g4
new file mode 100644
index 0000000000000000000000000000000000000000..93af095380a83f3e87b962cb7d81d2ccae8181a8
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/grammars/HTMLLexer.g4
@@ -0,0 +1,285 @@
+lexer grammar HTMLLexer;
+
+DJ_OPEN
+ : '{% '
+ ;
+
+DJ_CLOSE
+ : ' %}'
+ ;
+
+DJ_VARIABLE_OPEN
+ : '{{ '
+ ;
+
+DJ_VARIABLE_CLOSE
+ : ' }}'
+ ;
+
+JSON_BRACE_OPEN
+ : '{ '
+ ;
+
+JSON_BRACE_CLOSE
+ : ' }'
+ ;
+
+JSON_COMMA
+ : ','
+ ;
+
+JSON_QUOTES
+ : '"'
+ ;
+
+JSON_COLON
+ : ':'
+ ;
+
+DJ_COMMENT_OPEN
+ : '{# '
+ ;
+
+DJ_COMMENT_CLOSE
+ : ' #}'
+ ;
+
+DJ_FORCE_ESCAPE_FILTER
+ : '|force_escape'
+ ;
+
+DJ_NOW
+ : 'now '
+ ;
+
+DJ_NOW_FORMAT
+ : '"SHORT_DATETIME_FORMAT"'
+ | '"DATETIME_FORMAT"'
+ | '"DATE_FORMAT"'
+ | '"SHORT_DATE_FORMAT"'
+ ;
+
+DJ_DEBUG
+ : DJ_OPEN 'debug' DJ_CLOSE
+ ;
+
+DJ_WITH_KEYWORD
+ : 'with '
+ ;
+
+DJ_BLOCK_KEYWORD
+ : 'block '
+ ;
+
+DJ_FOR_KEYWORD
+ : 'for '
+ ;
+
+DJ_AS_KEYWORD
+ : 'as '
+ ;
+
+DJ_INCLUDE_KEYWORD
+ : 'include '
+ ;
+
+DJ_FOR_IN_KEYWORD
+ : ' in '
+ ;
+
+DJ_CYCLE_KEYWORD
+ : 'cycle '
+ ;
+
+DJ_FIRSTOF_KEYWORD
+ : 'firstof '
+ ;
+
+DJ_WITH_EQUALS
+ : '='
+ ;
+
+DJ_SPACE
+ : ' '
+ ;
+
+DJ_VARIABLE
+ : 'var1' | 'var2' | 'var3' | 'somename'
+ ;
+
+DJ_VALUE
+ : '"123"' | '""'
+ ;
+
+DJ_INCLUDE_FILENAME
+ : '"./snippet.html"'
+ ;
+
+DJ_END_WITH
+ : DJ_OPEN 'endwith' DJ_CLOSE
+ ;
+
+DJ_START_COMMENT
+ : DJ_OPEN 'comment' DJ_CLOSE
+ ;
+
+DJ_END_COMMENT
+ : DJ_OPEN 'endcomment' DJ_CLOSE
+ ;
+
+DJ_END_FOR_LOOP
+ : DJ_OPEN 'endfor' DJ_CLOSE
+ ;
+
+DJ_START_SPACELESS
+ : DJ_OPEN 'spaceless' DJ_CLOSE
+ ;
+
+DJ_END_SPACELESS
+ : DJ_OPEN 'endspaceless' DJ_CLOSE
+ ;
+
+DJ_TEMPLATE_TAG
+ : DJ_OPEN 'templatetag ' DJ_TEMPLATE_TAG_OPTION DJ_CLOSE
+ ;
+
+DJ_TEMPLATE_TAG_OPTION
+ : 'openblock' | 'closeblock' | 'openvariable' | 'closevariable' | 'openbrace' | 'closebrace' | 'opencomment' | 'closecomment'
+ ;
+
+DJ_END_BLOCK
+ : DJ_OPEN 'endblock' DJ_CLOSE
+ ;
+
+DJ_BLOCK_NAME
+ : 'name1' | 'name2' | 'name3'
+ ;
+
+JSON_FIELD_NAME
+ : 'name'
+ ;
+
+JSON_FIELD_NAMED_TYPE
+ : JSON_QUOTES 'type' JSON_QUOTES
+ ;
+
+JSON_FIELD_NAMED_NAME
+ : JSON_QUOTES 'name' JSON_QUOTES
+ ;
+
+JSON_FIELD_VALUE_BLOCK
+ : JSON_QUOTES 'block' JSON_QUOTES
+ ;
+
+JSON_FIELD_VALUE_INCLUDE
+ : JSON_QUOTES 'include' JSON_QUOTES
+ ;
+
+JSON_FIELD_STRING_VALUE
+ : 'value'
+ ;
+
+JSON_FIELD_LIST_VALUE
+ : '["value","value"]'
+ ;
+
+NEWLINE
+ : '\n'
+ ;
+
+TAG_OPEN
+ : '<' -> pushMode(TAG)
+ ;
+
+HTML_TEXT
+ : TAG_NameStartChar+
+ ;
+
+//
+// tag declarations
+//
+mode TAG;
+
+TAG_CLOSE
+ : '>' -> popMode
+ ;
+
+TAG_SLASH_CLOSE
+ : '/>' -> popMode
+ ;
+
+TAG_SLASH
+ : '/'
+ ;
+
+//
+// lexing mode for attribute values
+//
+TAG_EQUALS
+ : '=' -> pushMode(ATTVALUE)
+ ;
+
+TAG_NAME
+ : TAG_NameStartChar TAG_NameChar*
+ ;
+
+TAG_WHITESPACE
+ : [ ] -> skip
+ ;
+
+fragment
+HEXDIGIT
+ : [a-fA-F0-9]
+ ;
+
+fragment
+DIGIT
+ : [0-9]
+ ;
+
+fragment
+TAG_NameChar
+ : TAG_NameStartChar
+ | '-'
+ | '_'
+ | '.'
+ | DIGIT
+ ;
+
+fragment
+TAG_NameStartChar
+ : [a-zA-Z]
+ ;
+
+//
+// attribute values
+//
+mode ATTVALUE;
+
+// an attribute value may have spaces b/t the '=' and the value
+ATTVALUE_VALUE
+ : ATTRIBUTE -> popMode
+ ;
+
+ATTRIBUTE
+ : ATTCHARS
+ ;
+
+fragment ATTCHAR
+ : '-'
+ | '_'
+ | '.'
+ | '/'
+ | '+'
+ | ','
+ | '?'
+ | '='
+ | ':'
+ | ';'
+ | '#'
+ | [0-9a-zA-Z]
+ ;
+
+fragment ATTCHARS
+ : ATTCHAR+ ' '?
+ ;
diff --git a/cve/django/2022/CVE-2022-22818/grammars/HTMLParser.g4 b/cve/django/2022/CVE-2022-22818/grammars/HTMLParser.g4
new file mode 100644
index 0000000000000000000000000000000000000000..116448803de9be68e3c8ee326a5e09c8e982f273
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/grammars/HTMLParser.g4
@@ -0,0 +1,285 @@
+// TEST-PROCESS: {grammar}Parser.g4 {grammar}Lexer.g4 -o {tmpdir}
+// TEST-GENERATE: {grammar}Generator.{grammar}Generator -r htmlDocument -s {grammar}Generator.html_space_serializer -n 5 -o {tmpdir}/{grammar}G%d.html
+// TEST-GENERATE: {grammar}CustomGenerator.{grammar}CustomGenerator -r htmlDocument -s {grammar}Generator.html_space_serializer -n 5 -o {tmpdir}/{grammar}C%d.html --sys-path ../fuzzer/
+
+parser grammar HTMLParser;
+
+options { tokenVocab=HTMLLexer; language='Python3'; }
+
+@header {
+from copy import deepcopy
+}
+
+@parser::member {
+def _endOfHtmlElement(self):
+ pass
+
+def _endOfDjangoWithBlock(self):
+ pass
+
+def _endOfDjangoForLoop(self):
+ pass
+
+def _startOfDjangoWithBlock(self):
+ pass
+
+def _flushState(self):
+ pass
+
+def _hasAtLeastOneWithVariableDefined(self) -> bool:
+ return False
+
+def _hasAtLeastOneContextStringVariableDefined(self) -> bool:
+ return False
+
+def _hasAtLeastOneForLoopVariable(self) -> bool:
+ return False
+
+def _hasAtLeastOneContextListVariableDefined(self) -> bool:
+ return False
+
+def _hasAtLeastOneCycleVariableDefined(self) -> bool:
+ return False
+
+def _hasAtLeastOneDefinedVariable(self) -> bool:
+ return (self._hasAtLeastOneContextStringVariableDefined() or
+ self._hasAtLeastOneWithVariableDefined() or
+ self._hasAtLeastOneForLoopVariable() or
+ self._hasAtLeastOneCycleVariableDefined())
+
+is_in_comment_section: bool = False
+}
+
+htmlDocument
+ : {self._flushState()} djangoContext NEWLINE htmlElements+
+ ;
+
+htmlElements
+ : htmlElement htmlMisc
+ ;
+
+htmlElement
+ : TAG_OPEN open_tag=htmlTagName TAG_WHITESPACE htmlAttribute? TAG_CLOSE htmlContent TAG_OPEN TAG_SLASH htmlTagName {current.last_child = deepcopy($open_tag)} TAG_CLOSE {self._endOfHtmlElement()}
+ | django
+ ;
+
+django
+ : djangoSpaceless
+ | djangoWith
+ | djangoDebug
+ | djangoTemplateTag
+ | djangoNowTag
+ | djangoFirstOf
+ | {self._hasAtLeastOneContextListVariableDefined()}? djangoForLoop
+ | {self._hasAtLeastOneForLoopVariable()}? djangoCycle
+ | {self._hasAtLeastOneDefinedVariable()}? djangoVariable
+ | djangoBlock
+ | {not self.is_in_comment_section}? djangoIncludeTag {self.is_in_comment_section = True} djangoCommentedIncludingTemplate {self.is_in_comment_section = False}
+ | {not self.is_in_comment_section}? djangoOverriddenBlock {self.is_in_comment_section = True} djangoCommentedOverridingBlock {self.is_in_comment_section = False}
+ ;
+
+djangoDebug
+ : DJ_DEBUG
+ ;
+
+djangoWith
+ : {self._startOfDjangoWithBlock()} DJ_OPEN DJ_WITH_KEYWORD djangoWithVariables+ DJ_CLOSE htmlContent DJ_END_WITH {self._endOfDjangoWithBlock()}
+ ;
+
+djangoCommentedOverridingBlock
+ : DJ_START_COMMENT djangoCommentedOverridingBlockInfo htmlElement+ DJ_END_COMMENT
+ ;
+
+djangoCommentedIncludingTemplate
+ : DJ_START_COMMENT djangoCommentedIncludingTemplateInfo htmlElement+ DJ_END_COMMENT
+ ;
+
+djangoCommentedOverridingBlockInfo
+ : DJ_COMMENT_OPEN JSON_BRACE_OPEN
+ JSON_FIELD_NAMED_TYPE JSON_COLON JSON_FIELD_VALUE_BLOCK JSON_COMMA
+ JSON_FIELD_NAMED_NAME JSON_COLON JSON_QUOTES jsonOverridingBlockName JSON_QUOTES
+ JSON_BRACE_CLOSE DJ_COMMENT_CLOSE
+ ;
+
+djangoCommentedIncludingTemplateInfo
+ : DJ_COMMENT_OPEN JSON_BRACE_OPEN
+ JSON_FIELD_NAMED_TYPE JSON_COLON JSON_FIELD_VALUE_INCLUDE JSON_COMMA
+ JSON_FIELD_NAMED_NAME JSON_COLON JSON_QUOTES jsonIncludingTemplateName JSON_QUOTES
+ JSON_BRACE_CLOSE DJ_COMMENT_CLOSE
+ ;
+
+jsonOverridingBlockName
+ : DJ_BLOCK_NAME
+ ;
+
+jsonIncludingTemplateName
+ : JSON_FIELD_STRING_VALUE
+ ;
+
+djangoOverriddenBlock
+ : DJ_OPEN DJ_BLOCK_KEYWORD djangoOverriddenBlockName DJ_CLOSE htmlContent DJ_END_BLOCK
+ ;
+
+djangoIncludeTag
+ : DJ_OPEN DJ_INCLUDE_KEYWORD djangoIncludeFileNameWithQuotes DJ_CLOSE
+ ;
+
+djangoBlock
+ : DJ_OPEN DJ_BLOCK_KEYWORD djangoBlockName DJ_CLOSE htmlContent DJ_END_BLOCK
+ ;
+
+djangoSpaceless
+ : DJ_START_SPACELESS htmlContent DJ_END_SPACELESS
+ ;
+
+djangoNowTag
+ : DJ_OPEN DJ_NOW DJ_NOW_FORMAT DJ_CLOSE
+ ;
+
+djangoTemplateTag
+ : DJ_TEMPLATE_TAG
+ ;
+
+djangoForLoop
+ : DJ_OPEN DJ_FOR_KEYWORD djangoForLoopVariableName DJ_FOR_IN_KEYWORD djangoDefinedContextListVariable DJ_CLOSE NEWLINE htmlContent NEWLINE DJ_END_FOR_LOOP {self._endOfDjangoForLoop()}
+ ;
+
+djangoCycle
+ : DJ_OPEN DJ_CYCLE_KEYWORD djangoCycleValue DJ_SPACE (djangoCycleValue DJ_SPACE)+ (DJ_AS_KEYWORD djangoCycleVariableName)? DJ_CLOSE
+ ;
+
+djangoCycleValue
+ : {self._hasAtLeastOneContextStringVariableDefined()}? djangoDefinedContextVariable
+ | {self._hasAtLeastOneWithVariableDefined()}? djangoDefinedWithVariable
+ | djangoCycleStringValue
+ ;
+
+djangoWithVariables
+ : djangoWithVariable DJ_WITH_EQUALS djangoWithVariableValue DJ_SPACE
+ ;
+
+djangoFirstOf
+ : DJ_OPEN DJ_FIRSTOF_KEYWORD (djangoFirstOfVariable DJ_SPACE)+ DJ_VALUE? DJ_CLOSE
+ ;
+
+djangoFirstOfVariable
+ : DJ_VARIABLE
+ | {self._hasAtLeastOneDefinedVariable()}? djangoDefinedVariable
+ ;
+
+djangoVariable
+ : DJ_VARIABLE_OPEN djangoDefinedVariable DJ_FORCE_ESCAPE_FILTER DJ_VARIABLE_CLOSE
+ ;
+
+djangoDefinedVariable
+ : {self._hasAtLeastOneWithVariableDefined()}? djangoDefinedWithVariable
+ | {self._hasAtLeastOneContextStringVariableDefined()}? djangoDefinedContextVariable
+ | {self._hasAtLeastOneForLoopVariable()}? djangoDefinedLoopVariable
+ | {self._hasAtLeastOneCycleVariableDefined()}? djangoDefinedCycleVariable
+ ;
+
+djangoIncludeFileNameWithQuotes
+ : DJ_INCLUDE_FILENAME
+ ;
+
+djangoDefinedLoopVariable
+ : DJ_VARIABLE
+ ;
+
+djangoForLoopVariableName
+ : DJ_VARIABLE
+ ;
+
+djangoDefinedContextListVariable
+ : DJ_VARIABLE
+ ;
+
+djangoDefinedWithVariable
+ : DJ_VARIABLE
+ ;
+
+djangoDefinedContextVariable
+ : DJ_VARIABLE
+ ;
+
+djangoDefinedCycleVariable
+ : DJ_VARIABLE
+ ;
+
+djangoCycleVariableName
+ : DJ_VARIABLE
+ ;
+
+djangoWithVariable
+ : DJ_VARIABLE
+ ;
+
+djangoCycleStringValue
+ : DJ_VALUE
+ ;
+
+djangoWithVariableValue
+ : {self._hasAtLeastOneContextStringVariableDefined()}? djangoDefinedContextVariable
+ | DJ_VALUE
+ ;
+
+htmlContent
+ : htmlChardata? (htmlElement htmlChardata?)*
+ ;
+
+htmlAttribute
+ : htmlAttributeName TAG_EQUALS htmlAttributeValue
+ ;
+
+djangoContext
+ : DJ_COMMENT_OPEN JSON_BRACE_OPEN djangoContextVariableDefinition (JSON_COMMA djangoContextVariableDefinition)* JSON_BRACE_CLOSE DJ_COMMENT_CLOSE
+ ;
+
+djangoContextVariableDefinition
+ : JSON_QUOTES jsonFieldName JSON_QUOTES JSON_COLON jsonFieldValue
+ ;
+
+jsonFieldName
+ : JSON_FIELD_NAME
+ ;
+
+jsonFieldValue
+ : JSON_QUOTES jsonStringValue JSON_QUOTES
+ | jsonListValue
+ ;
+
+jsonStringValue
+ : JSON_FIELD_STRING_VALUE
+ ;
+
+jsonListValue
+ : JSON_FIELD_LIST_VALUE
+ ;
+
+htmlAttributeName
+ : TAG_NAME
+ ;
+
+htmlAttributeValue
+ : ATTVALUE_VALUE
+ ;
+
+djangoOverriddenBlockName
+ : DJ_BLOCK_NAME
+ ;
+
+djangoBlockName
+ : DJ_BLOCK_NAME
+ ;
+
+htmlTagName
+ : TAG_NAME
+ ;
+
+htmlChardata
+ : HTML_TEXT NEWLINE
+ ;
+
+htmlMisc
+ : NEWLINE
+ ;
diff --git a/cve/django/2022/CVE-2022-22818/grammars/fuzzer/HTMLCustomGenerator.py b/cve/django/2022/CVE-2022-22818/grammars/fuzzer/HTMLCustomGenerator.py
new file mode 100644
index 0000000000000000000000000000000000000000..57119c91325fa419e2f3e96c9d375801c0816188
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/grammars/fuzzer/HTMLCustomGenerator.py
@@ -0,0 +1,264 @@
+import json
+import random
+
+from os.path import dirname, join
+import string
+from typing import List
+
+from grammarinator.runtime import *
+
+from grammars.fuzzer.HTMLGenerator import HTMLGenerator
+from itertools import chain
+
+with open(join(dirname(__file__), 'html.json')) as f:
+ tags = json.load(f)
+
+tag_names = list(tags.keys())
+
+
+class HTMLCustomGenerator(HTMLGenerator):
+
+ django_context = dict() # Контекст шаблона.
+ last_json_field_name = [] # Буфер для создания новой переменной.
+ attr_stack = [] # Валидные значения html-атрибутов.
+ tag_stack = [] # Валидные html-тэги и их атрибуты.
+ for_variables_stack = [] # Имена переменных, которые используются в for-циклах.
+ cycle_variables_stack = [] # Имена, которые используются для сохранения значения cycle внутри for-циклов.
+ django_block_names: List[str] = [] # Уникальные имена блоков.
+ django_variables_block_stack: List[List[str]] = [] # Введённые через with-блоки переменные django.
+ overridden_block_names = [] # Имена блоков для переопределения.
+ include_filename: str = "" # Имя включаемого файла.
+
+ # Очищение всего перед следующим тестом.
+ def _flushState(self):
+ self.django_context = dict()
+ self.last_json_field_name = []
+ self.attr_stack = []
+ self.tag_stack = []
+ self.for_variables_stack = []
+ self.cycle_variables_stack = []
+ self.django_block_names = []
+ self.django_variables_block_stack = []
+ self.overridden_block_names = []
+ self.include_filename = ""
+
+ # Получение случайного значения для контекстной переменной django.
+ def __getRandomStringValue(self):
+ return random.choice(["123", "", "
HELLO!
"])
+
+ # Получение случайно сгенерированного списка.
+ def __getRandomListValue(self):
+ i: int = random.randint(1, 10)
+ return [self.__getRandomStringValue() for k in range(i)]
+
+ # Получение контекстных переменных, значение которых является указанным типом.
+ def __getContextVariablesOfCertainType(self, type):
+ return list(item[0] for item in self.django_context.items() if isinstance(item[1], type))
+
+ @staticmethod
+ def __toSingleList(list_of_lists):
+ return list(chain.from_iterable(list_of_lists))
+
+ # Получение всех переменных определенных в with-блоках.
+ def __getAllDefinedWithVariables(self):
+ return self.__toSingleList(self.django_variables_block_stack)
+
+ # Получение всех переменных определённых в cycle-тэгах.
+ def __getAllDefinedCycleVariables(self):
+ return self.__toSingleList(self.cycle_variables_stack)
+
+ # Генерация уникального имени переменной django-контекста.
+ def jsonFieldName(self, parent=None):
+ current = UnparserRule(name='jsonFieldName', parent=parent)
+ name_length = 1
+ name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length))
+ while name in self.django_context.keys():
+ name_length += 1
+ name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length))
+ UnlexerRule(src=name, parent=current)
+ self.last_json_field_name.append(name)
+ return current
+
+ # Переменная django-контекста как строка: генерация + сохранение в контекст для дальнейшего использования.
+ def jsonStringValue(self, parent=None):
+ current = UnparserRule(name='jsonStringValue', parent=parent)
+ value = self.__getRandomStringValue()
+ UnlexerRule(src=value, parent=current)
+ new_field_name = self.last_json_field_name.pop()
+ self.django_context[new_field_name] = value
+ return current
+
+ # Переменная django-контекста как список строк: генерация + сохранение в контекст для дальнейшего использования.
+ def jsonListValue(self, parent=None):
+ current = UnparserRule(name='jsonListValue', parent=parent)
+ value = self.__getRandomListValue()
+ UnlexerRule(src=json.dumps(value), parent=current)
+ new_field_name = self.last_json_field_name.pop()
+ self.django_context[new_field_name] = value
+ return current
+
+ # Валидные имена тэгов.
+ def htmlTagName(self, parent=None):
+ current = UnparserRule(name='htmlTagName', parent=parent)
+ name = random.choice(tags[self.tag_stack[-1]]['children'] or tag_names if self.tag_stack else tag_names)
+ self.tag_stack.append(name)
+ UnlexerRule(src=name, parent=current)
+ return current
+
+ # Уникальные имена блоков.
+ def djangoBlockName(self, parent=None):
+ current = UnparserRule(name='djangoBlockName', parent=parent)
+ name_length = 1
+ name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length))
+ while name in self.django_block_names:
+ name_length += 1
+ name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length))
+ self.django_block_names.append(name)
+ UnlexerRule(src=name, parent=current)
+ return current
+
+ # Уникальные имена блоков + сохранение в стэк имени, так как блок будет переопределён.
+ def djangoOverriddenBlockName(self, parent=None):
+ current = UnparserRule(name='djangoOverriddenBlockName', parent=parent)
+ name_length = 1
+ name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length))
+ while name in self.django_block_names:
+ name_length += 1
+ name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length))
+ self.django_block_names.append(name)
+ self.overridden_block_names.append(name) # абсолютно такой же как djangoBlockName за исключением вот этой вот строчки.
+ UnlexerRule(src=name, parent=current)
+ return current
+
+ # Сохранение имён переменных django + проверка, чтобы они были уникальными.
+ def djangoWithVariable(self, parent=None):
+ current = UnparserRule(name='djangoWithVariable', parent=parent)
+ name = 'var' + str(sum(map(len, self.django_variables_block_stack)))
+ self.django_variables_block_stack[-1].append(name)
+ UnlexerRule(src=name, parent=current)
+ return current
+
+ # Существующее имя django-переменной из with-блока.
+ def djangoDefinedWithVariable(self, parent=None):
+ current = UnparserRule(name='djangoDefinedWithVariable', parent=parent)
+ django_defined_variable_name = random.choice(self.__getAllDefinedWithVariables())
+ UnlexerRule(src=django_defined_variable_name, parent=current)
+ return current
+
+ # Существующее имя django-переменной из контекста.
+ def djangoDefinedContextVariable(self, parent=None):
+ current = UnparserRule(name='djangoDefinedContextVariable', parent=parent)
+ django_defined_variable_name = random.choice(self.__getContextVariablesOfCertainType(str))
+ UnlexerRule(src=django_defined_variable_name, parent=current)
+ return current
+
+ # Существующее имя django-переменной из контекста, которая является списком.
+ def djangoDefinedContextListVariable(self, parent=None):
+ current = UnparserRule(name='djangoDefinedContextListVariable', parent=parent)
+ django_defined_variable_name = random.choice(self.__getContextVariablesOfCertainType(list))
+ UnlexerRule(src=django_defined_variable_name, parent=current)
+ return current
+
+ # Генерируем + сохраняем имя переменной for-loop.
+ def djangoForLoopVariableName(self, parent=None):
+ current = UnparserRule(name='djangoForLoopVariableName', parent=parent)
+ name = 'loop_var' + str(len(self.for_variables_stack))
+ UnlexerRule(src=name, parent=current)
+ self.for_variables_stack.append(name)
+ self.cycle_variables_stack.append([])
+ return current
+
+ # Генерируем + сохраняем имя переменной cycle. Это может возникнуть только внутри цикла, поэтому len(cycle_variables_stack) >0.
+ def djangoCycleVariableName(self, parent=None):
+ current = UnparserRule(name='djangoCycleVariableName', parent=parent)
+ name = 'cycle_var' + str(sum(map(len, self.cycle_variables_stack)))
+ UnlexerRule(src=name, parent=current)
+ self.cycle_variables_stack[-1].append(name)
+ return current
+
+ # Существующее имя cycle переменной.
+ def djangoDefinedCycleVariable(self, parent=None):
+ current = UnparserRule(name='djangoDefinedCycleVariable', parent=parent)
+ django_defined_variable_name = random.choice(self.__getAllDefinedCycleVariables())
+ UnlexerRule(src=django_defined_variable_name, parent=current)
+ return current
+
+ # Имя for-loop переменной.
+ def djangoDefinedLoopVariable(self, parent=None):
+ current = UnparserRule(name='djangoDefinedLoopVariable', parent=parent)
+ django_defined_variable_name = random.choice(self.for_variables_stack)
+ UnlexerRule(src=django_defined_variable_name, parent=current)
+ return current
+
+ # Имя переопределяемого блока.
+ def jsonOverridingBlockName(self, parent=None):
+ current = UnparserRule(name='jsonOverridingBlockName', parent=parent)
+ django_defined_name = self.overridden_block_names.pop()
+ UnlexerRule(src=django_defined_name, parent=current)
+ return current
+
+ # Имя включаемого файла.
+ def jsonIncludingTemplateName(self, parent=None):
+ current = UnparserRule(name='jsonIncludingTemplateName', parent=parent)
+ UnlexerRule(src=self.include_filename, parent=current)
+ self.include_filename = ""
+ return current
+
+ # Имя включаемого файла: генерация + сохранение.
+ def djangoIncludeFileNameWithQuotes(self, parent=None):
+ current = UnparserRule(name='djangoIncludeFileNameWithQuotes', parent=parent)
+ name_length = 10
+ self.include_filename = "./t_" + ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(name_length)) + ".html"
+ UnlexerRule(src='"' + self.include_filename + '"', parent=current)
+ return current
+
+ # Валидное имя атрибута.
+ def htmlAttributeName(self, parent=None):
+ current = UnparserRule(name='htmlAttributeName', parent=parent)
+ name = random.choice(list(tags[self.tag_stack[-1]]['attributes'].keys()) or ['""'])
+ self.attr_stack.append(name)
+ UnlexerRule(src=name, parent=current)
+ return current
+
+ # Валидные значения атрибутов для текущего тэга и атрибутов.
+ def htmlAttributeValue(self, parent=None):
+ current = UnparserRule(name='htmlAttributeValue', parent=parent)
+ UnlexerRule(src=random.choice(tags[self.tag_stack[-1]]['attributes'].get(self.attr_stack.pop(), ['""']) or ['""']), parent=current)
+ return current
+
+ # Конец определения html-тэга.
+ def _endOfHtmlElement(self):
+ self.tag_stack.pop()
+
+ # Начало django блока with.
+ def _startOfDjangoWithBlock(self):
+ self.django_variables_block_stack.append([])
+
+ # Конец django блока with.
+ def _endOfDjangoWithBlock(self):
+ self.django_variables_block_stack.pop()
+
+ # Конец django for-цикла.
+ def _endOfDjangoForLoop(self):
+ self.for_variables_stack.pop()
+ self.cycle_variables_stack.pop()
+
+ # Есть ли хотя бы 1 определенная django-переменная из with-блока, которую можно использовать для вставки.
+ def _hasAtLeastOneWithVariableDefined(self) -> bool:
+ return len(self.django_variables_block_stack) > 0
+
+ # Есть ли хотя бы 1 определённая строковая django-переменная из контекста.
+ def _hasAtLeastOneContextStringVariableDefined(self) -> bool:
+ return len(self.__getContextVariablesOfCertainType(str)) > 0
+
+ # Есть ли хотя бы 1 определённая django-переменная из контекста, которая является списком.
+ def _hasAtLeastOneContextListVariableDefined(self) -> bool:
+ return len(self.__getContextVariablesOfCertainType(list)) > 0
+
+ # Есть ли хотя бы 1 переменная из цикла.
+ def _hasAtLeastOneForLoopVariable(self) -> bool:
+ return len(self.for_variables_stack) > 0
+
+ # Есть хотя бы 1 cycle-переменная.
+ def _hasAtLeastOneCycleVariableDefined(self) -> bool:
+ return len(self.__getAllDefinedCycleVariables()) > 0
diff --git a/cve/django/2022/CVE-2022-22818/grammars/fuzzer/html.json b/cve/django/2022/CVE-2022-22818/grammars/fuzzer/html.json
new file mode 100644
index 0000000000000000000000000000000000000000..b0449a46128e8e119b5ee8bec2cd40e6332ae1b6
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/grammars/fuzzer/html.json
@@ -0,0 +1,55 @@
+{
+ "a": {
+ "attributes": {
+ "href": ["https://github.com/renatahodovan/grammarinator", "https://github.com/renatahodovan/fuzzinator"]
+ },
+ "children": []
+ },
+ "address": {
+ "attributes": {
+ "contenteditable": ["true", "false"],
+ "dir": ["ltr", "rtl"],
+ "draggable": ["true", "false"]
+ },
+ "children": ["a", "address", "article"]
+ },
+ "article": {
+ "attributes": {
+ "style": ["\"color: #2ecc71\""]
+ },
+ "children": ["a", "article"]
+ },
+ "base": {
+ "attributes": {
+ "contenteditable": ["true", "false"],
+ "dir": ["ltr", "rtl"],
+ "draggable": ["true", "false"],
+ "translate": ["true", "false"]
+ },
+ "children": []
+ },
+ "body": {
+ "attributes": {
+ "translate": ["true", "false"]
+ },
+ "children": ["a", "address", "article"]
+ },
+ "head": {
+ "attributes": {
+ "contenteditable": ["true", "false"],
+ "dir": ["ltr", "rtl"],
+ "draggable": ["true", "false"],
+ "translate": ["true", "false"]
+ },
+ "children": ["base"]
+ },
+ "html": {
+ "attributes": {
+ "contenteditable": ["true", "false"],
+ "dir": ["ltr", "rtl"],
+ "draggable": ["true", "false"],
+ "translate": ["true", "false"]
+ },
+ "children": ["head", "body"]
+ }
+}
diff --git a/cve/django/2022/CVE-2022-22818/manage.py b/cve/django/2022/CVE-2022-22818/manage.py
new file mode 100755
index 0000000000000000000000000000000000000000..a7da6671aab8568fe454d9e4bccdcdf661b6f7ff
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ """Run administrative tasks."""
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/cve/django/2022/CVE-2022-22818/mylogger.py b/cve/django/2022/CVE-2022-22818/mylogger.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c731df35a8799deb228ba98a1d19a3e31c4b3c4
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/mylogger.py
@@ -0,0 +1,15 @@
+import logging
+
+
+def setup_logger(logger: logging.Logger, to_level: int = logging.DEBUG, newline: str = ""):
+ """Устанавливает базовые настройки логгера.
+
+ :param logger: Логгер.
+ :type logger: logging.Logger
+ """
+ console_handler = logging.StreamHandler()
+ FORMAT = newline + "%(asctime)s — %(name)s — %(levelname)s — %(message)s"
+ format = logging.Formatter(FORMAT, validate=True)
+ console_handler.setFormatter(format)
+ logger.addHandler(console_handler)
+ logger.setLevel(to_level)
\ No newline at end of file
diff --git a/cve/django/2022/CVE-2022-22818/mysite/__init__.py b/cve/django/2022/CVE-2022-22818/mysite/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/cve/django/2022/CVE-2022-22818/mysite/asgi.py b/cve/django/2022/CVE-2022-22818/mysite/asgi.py
new file mode 100644
index 0000000000000000000000000000000000000000..674a5a5af4d12fd968808f8c390a03392260c083
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/mysite/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for mysite project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
+
+application = get_asgi_application()
diff --git a/cve/django/2022/CVE-2022-22818/mysite/settings.py b/cve/django/2022/CVE-2022-22818/mysite/settings.py
new file mode 100644
index 0000000000000000000000000000000000000000..578f297288345cf011f7aa9eef7f922a80955aca
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/mysite/settings.py
@@ -0,0 +1,124 @@
+"""
+Django settings for mysite project.
+
+Generated by 'django-admin startproject' using Django 4.0.1.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.0/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/4.0/ref/settings/
+"""
+
+from pathlib import Path
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure-ya3vdv0#u#e-=t1o4#)^)i09gweg90*a(ew=*&gnkb-1$7)r7n'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'polls.apps.PollsConfig',
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+]
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'mysite.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'mysite.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': BASE_DIR / 'db.sqlite3',
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/4.0/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/4.0/howto/static-files/
+
+STATIC_URL = 'static/'
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
diff --git a/cve/django/2022/CVE-2022-22818/mysite/urls.py b/cve/django/2022/CVE-2022-22818/mysite/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb270f7971c33fc5b489fb0963c969f318171abc
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/mysite/urls.py
@@ -0,0 +1,21 @@
+"""mysite URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/4.0/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.urls import include, path
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path, include
+
+urlpatterns = [
+ path('', include('polls.urls')),
+]
diff --git a/cve/django/2022/CVE-2022-22818/mysite/wsgi.py b/cve/django/2022/CVE-2022-22818/mysite/wsgi.py
new file mode 100644
index 0000000000000000000000000000000000000000..48d46fe4cf84ba2e7d4aa9f808a849ba9dc495c7
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/mysite/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for mysite project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
+
+application = get_wsgi_application()
diff --git a/cve/django/2022/CVE-2022-22818/polls/__init__.py b/cve/django/2022/CVE-2022-22818/polls/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/cve/django/2022/CVE-2022-22818/polls/admin.py b/cve/django/2022/CVE-2022-22818/polls/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/polls/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/cve/django/2022/CVE-2022-22818/polls/apps.py b/cve/django/2022/CVE-2022-22818/polls/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a5f94ca17168cdc07caa88325fbb36421bf4eef
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/polls/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class PollsConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'polls'
diff --git a/cve/django/2022/CVE-2022-22818/polls/migrations/__init__.py b/cve/django/2022/CVE-2022-22818/polls/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/cve/django/2022/CVE-2022-22818/polls/models.py b/cve/django/2022/CVE-2022-22818/polls/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..71a836239075aa6e6e4ecb700e9c42c95c022d91
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/polls/models.py
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.
diff --git a/cve/django/2022/CVE-2022-22818/polls/templates/polls/base.html b/cve/django/2022/CVE-2022-22818/polls/templates/polls/base.html
new file mode 100644
index 0000000000000000000000000000000000000000..35dd4e3e9a34646abf5be4dc3616debb261174d4
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/polls/templates/polls/base.html
@@ -0,0 +1,23 @@
+
+
+Base part
+
+{% for var2 in list_var %}
+
+{% with var0="string_value" %}
+
+{% block main %}
+
+Var0: {{ var0 }}
{# with #}
+
+Var1: {{ var1 }}
{# контекст #}
+
+Var2: {{ var2 }}
{# for #}
+
+{% endblock %}
+
+{% endwith %}
+
+{% endfor %}
+
+
diff --git a/cve/django/2022/CVE-2022-22818/polls/templates/polls/index.html b/cve/django/2022/CVE-2022-22818/polls/templates/polls/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..5ecf8e7d9ca86d2aebb8d81ad2a01fbc9eec6830
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/polls/templates/polls/index.html
@@ -0,0 +1,6 @@
+{% with message1="" %}
+
+ {% debug %}
+
+Visible page.
+{% endwith %}
\ No newline at end of file
diff --git a/cve/django/2022/CVE-2022-22818/polls/templates/polls/inherited.html b/cve/django/2022/CVE-2022-22818/polls/templates/polls/inherited.html
new file mode 100644
index 0000000000000000000000000000000000000000..fe6d3ca7c0b748596dce1b086351af13b0f4020c
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/polls/templates/polls/inherited.html
@@ -0,0 +1,13 @@
+{% extends './base.html' %}
+
+{% block main %}
+
+Inherited part
+
+Var0: {{ var0 }}
{# with #}
+
+Var1: {{ var1 }}
{# контекст #}
+
+Var2: {{ var2 }}
{# for #}
+
+{% endblock %}
diff --git a/cve/django/2022/CVE-2022-22818/polls/tests.py b/cve/django/2022/CVE-2022-22818/polls/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/polls/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/cve/django/2022/CVE-2022-22818/polls/urls.py b/cve/django/2022/CVE-2022-22818/polls/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ef24d9717f6abdd8d356990d19b5c92b2ed0261
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/polls/urls.py
@@ -0,0 +1,7 @@
+from django.urls import path
+
+from . import views
+
+urlpatterns = [
+ path('', views.index, name='index'),
+]
\ No newline at end of file
diff --git a/cve/django/2022/CVE-2022-22818/polls/views.py b/cve/django/2022/CVE-2022-22818/polls/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9cd37938768c5a8e8f75066fece535569ed8d80
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/polls/views.py
@@ -0,0 +1,8 @@
+#from django.shortcuts import render
+from django.template import loader
+# Create your views here.
+from django.http import HttpResponse
+from django.shortcuts import render
+
+def index(request):
+ return render(request, 'polls/inherited.html', context={"var1": "string_another", "list_var": ["string_absolutely_new"]})
diff --git a/cve/django/2022/CVE-2022-22818/runfuzz.py b/cve/django/2022/CVE-2022-22818/runfuzz.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ab760822be212c9bf366bebf2ccf043c1824262
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/runfuzz.py
@@ -0,0 +1,116 @@
+import django
+import os
+import logging
+import subprocess
+from django.conf import settings
+import django
+from django.template import Template, Context, loader
+from django.template.loader import get_template
+from selen import Driver
+from djangocontext import ContextLoader
+from progress.bar import IncrementalBar
+from grammarinator.generate import *
+from multiprocessing import Pool
+from mylogger import setup_logger
+
+logger = logging.getLogger("Fuzzer")
+
+def django_setup():
+ settings.configure(TEMPLATES=[
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': ['./polls/templates/polls'],
+ }
+ ])
+ django.setup()
+
+
+def generate_tests(num: int, g: Generator):
+ bar = IncrementalBar('Generate', max=num)
+ for i in range(num):
+ g(i)
+ bar.next()
+ bar.finish()
+ return True
+
+
+def check_test(num: int, d: Driver) -> bool:
+ """Проверяет тесты.
+
+ :param num: Кол-во тестов.
+ :type num: int
+ :param d: Драйвер проверки.
+ :type d: Driver
+ :return: True если нашёл.
+ :rtype: bool
+ """
+ found: bool = False
+ bar = IncrementalBar('Check', max=num)
+ for i in range(num):
+ template_filepath: str = f"./polls/templates/polls/test_{i}.html"
+ output_rendered_name = f"./polls/templates/polls/rendered_test_{i}.html"
+ if not found:
+ ctx = ContextLoader(template_filepath)
+ ctx.create_and_modify_files_if_need()
+ t = get_template(f'test_{i}.html')
+ with open(output_rendered_name, "w") as text_file:
+ text_file.write(t.render(ctx.get_context()))
+ # if d.is_template_matched(template_filepath):
+ if d.is_alert_present(output_rendered_name):
+ bar.finish()
+ found = True
+ logger.info("Found!!!")
+ continue
+ bar.next()
+ ctx.remove_created_files()
+ if os.path.exists(output_rendered_name):
+ os.remove(output_rendered_name)
+ os.remove(template_filepath)
+
+ if not found:
+ bar.finish()
+ return found
+
+
+def run():
+ d = Driver()
+ g = Generator(generator='grammars.fuzzer.HTMLCustomGenerator.HTMLCustomGenerator', rule='htmlDocument', out_format='/home/alex/Документы/django-example/polls/templates/polls/test_%d.html',
+ model='grammarinator.runtime.DefaultModel', max_depth=60, cleanup=False)
+ django_setup()
+ num_of_tests = 20
+ found: bool = False
+
+ while not found:
+ logger.info("Creating new pool...")
+ generate_tests(num_of_tests, g)
+ found = check_test(num_of_tests, d)
+ if not found:
+ logger.info(f"Not found. Have run {num_of_tests} tests...")
+
+def prepare_fuzzer():
+ logger.info("Preparing fuzzer for generating tests...")
+ HTMLGenerator_modify_time = os.path.getmtime("grammars/fuzzer/HTMLGenerator.py")
+ HTMLLexer_modify_time = os.path.getmtime("grammars/HTMLLexer.g4")
+ HTMLParser_modify_time = os.path.getmtime("grammars/HTMLParser.g4")
+ if HTMLGenerator_modify_time > HTMLLexer_modify_time and HTMLGenerator_modify_time > HTMLParser_modify_time:
+ logger.info("Will not run grammarinator-process as grammar is already ready...")
+ return
+ result = subprocess.run(["grammarinator-process", "grammars/HTMLLexer.g4", "grammars/HTMLParser.g4", "-o", "grammars/fuzzer"], capture_output=True, text=True, check=True, timeout=10)
+ logger.info("Fuzzer prepared and grammar is ready!")
+ logger.debug('output: %s', result.stdout)
+ logger.debug('error: %s', result.stderr)
+
+
+def clear_directory():
+ templates_directory = "./polls/templates/polls"
+ only_files = [f for f in os.listdir(templates_directory) if os.path.isfile(os.path.join(templates_directory, f))]
+ for file in filter(lambda x: (x.startswith("test_") or x.startswith("rendered_test_") or x.startswith("t_")) and x.endswith(".html"), only_files):
+ logger.info("Found file from previous run: '%s'. Will be deleted!", file)
+ os.remove(os.path.join(templates_directory, file))
+
+if __name__ == "__main__":
+ setup_logger(logger)
+ setup_logger(logging.getLogger("ContextLoader"), logging.WARNING)
+ prepare_fuzzer()
+ clear_directory()
+ run()
diff --git a/cve/django/2022/CVE-2022-22818/selen.py b/cve/django/2022/CVE-2022-22818/selen.py
new file mode 100644
index 0000000000000000000000000000000000000000..8fabe6672d0b38fe9557033477a5ba9dbad3112d
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/selen.py
@@ -0,0 +1,55 @@
+import logging
+from selenium import webdriver as wd
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.common.exceptions import UnexpectedAlertPresentException
+from webdriver_manager.chrome import ChromeDriverManager
+from selenium.common.exceptions import NoAlertPresentException
+import os
+from mylogger import setup_logger
+
+class Driver:
+ logger = logging.getLogger("Driver")
+
+ def __init__(self):
+ setup_logger(self.logger, logging.INFO, "\n")
+ self.driver = wd.Chrome(ChromeDriverManager().install())
+ self.wait = WebDriverWait(self.driver, 15)
+
+ def is_alert_present(self, relative_path):
+ full_path = os.path.abspath(relative_path)
+ self.logger.debug("Testing rendered html at path %s.", full_path)
+ try:
+ self.driver.get(f"file://{full_path}")
+ try:
+ alert = self.driver.switch_to.alert
+ if alert is None:
+ return False
+ self.logger.warning("Found alert at: %s", relative_path)
+ return True
+ except NoAlertPresentException:
+ return False
+ except UnexpectedAlertPresentException:
+ self.logger.warning("UnexpectedAlertPresentException at: %s", relative_path)
+ return True
+
+ def is_template_matched(self, template_filepath) -> bool:
+ """Возвращает True если файл шаблона соответствует искомому.
+
+ Полезно, если надо проверить что грамматика может генерировать шаблоны
+ определённой структуры.
+
+ :param template_filepath: Путь до файла шаблона.
+ :type template_filepath: str
+ :rtype: bool
+ """
+ strings: list = []
+ with open(template_filepath, "r") as text_file:
+ strings = text_file.readlines()
+
+ searching_keyword = " comment "
+ result: bool = any(map(lambda s: s.count(searching_keyword), strings))
+ if result:
+ self.logger.warning("Found searching template file at: %s", template_filepath)
+ return result
\ No newline at end of file
diff --git a/cve/django/2022/CVE-2022-22818/tests/README.md b/cve/django/2022/CVE-2022-22818/tests/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..80654850009610eb5680307df6769cda30706688
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/tests/README.md
@@ -0,0 +1,11 @@
+# Тесты
+
+для проверки работы отдельных скриптов.
+
+## Запуск
+
+Из корневой директории:
+
+```bash
+python tests/context.py
+```
\ No newline at end of file
diff --git a/cve/django/2022/CVE-2022-22818/tests/context.py b/cve/django/2022/CVE-2022-22818/tests/context.py
new file mode 100644
index 0000000000000000000000000000000000000000..4867888a62154d6f0ea446fc5ad8d798505b6e76
--- /dev/null
+++ b/cve/django/2022/CVE-2022-22818/tests/context.py
@@ -0,0 +1,33 @@
+import sys
+sys.path.append('.')
+import unittest
+from djangocontext import ContextLoader, CommentSection
+
+
+class TestContextLoader(unittest.TestCase):
+ def setUp(self):
+ self.loader = ContextLoader(None)
+
+ def test_get_context(self):
+ self.loader.ctx_json_data = "{ \"hello\": 123 }"
+ self.assertEqual(self.loader.get_context(), {"hello": 123})
+
+ def test_get_sections(self):
+ self.loader.text = """
+ {% block Z %}{% endblock %}{% comment %}{# { "type":"block","name":"Z" } #}GKNUM
+ {% with var0=X %}{% block R %}xR
+ {% debug %}{% endblock %}D
+ {% endwith %}{% endcomment %}
+ """
+ sections = self.loader.get_sections()
+ self.assertEqual(len(sections), 1)
+ section: CommentSection = (sections[0])
+ self.assertEqual(section.name, "Z")
+ self.assertEqual(section.type, "block")
+ self.assertEqual(section.inner, """GKNUM
+ {% with var0=X %}{% block R %}xR
+ {% debug %}{% endblock %}D
+ {% endwith %}""")
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/cve/django/2022/yaml/CVE-2022-22818.yaml b/cve/django/2022/yaml/CVE-2022-22818.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..0f6df8755d0ce30f7ae21eccb8dce1ab6ce1022a
--- /dev/null
+++ b/cve/django/2022/yaml/CVE-2022-22818.yaml
@@ -0,0 +1,22 @@
+id: CVE-2022-22818
+source:
+ https://github.com/Prikalel/django-xss-example
+info:
+ name: Django 是一个高级的 Python 网络框架,可以快速开发安全和可维护的网站。由经验丰富的开发者构建,Django 负责处理网站开发中麻烦的部分,因此你可以专注于编写应用程序,而无需重新开发。 它是免费和开源的,有活跃繁荣的社区,丰富的文档,以及很多免费和付费的解决方案。
+ severity: MEDIUM
+ description: |
+ Django 2.2(2.2.27之前)、3.2(3.2.12之前)和4.0(4.0.2之前)中的{% debug %}模板标签没有正确编码当前上下文。这可能导致XSS。
+ scope-of-influence:
+ Django 2.2.0 prior to 2.2.27
+ Django 3.2.0 prior to 3.2.12
+ Django 4.0.0 prior to 4.0.2
+ reference:
+ - https://nvd.nist.gov/vuln/detail/CVE-2022-22818
+ classification:
+ cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
+ cvss-score: 6.1
+ cve-id: CVE-2022-22818
+ cwe-id: CWE-79
+ cnvd-id: None
+ kve-id: None
+ tags: XSS
\ No newline at end of file
diff --git a/openkylin_list.yaml b/openkylin_list.yaml
index 7128c8a654f0511f8b057f1d715266e718f048c2..3712ef5f55bb9c0562df72d8e5c7b6b3a47cb0d9 100644
--- a/openkylin_list.yaml
+++ b/openkylin_list.yaml
@@ -38,7 +38,9 @@ cve:
apache-unomi:
- CVE-2020-13942
apache-struts:
- - CVE-2019-0230
+ - CVE-2019-0230
+ Django:
+ - CVE-2022-22818
Influx-DB:
- CVE-2019-20933
linux-kernel: