Skip to main content

Поддержка SARIF для проверки кода

Убедитесь, что ваши SARIF-файлы от сторонних инструментов соответствуют стандартам GitHub.

Кто может использовать эту функцию?

Code scanning доступен для следующих типов репозитория:

  • Общедоступные репозитории для GitHub.com
  • Репозитории, принадлежащие организации, на GitHub Team, GitHub Enterprise Cloud или GitHub Enterprise Server, с включённым GitHub Code Security .

GitHub может парсировать файлы SARIF, созданные сторонними инструментами, для отображения code scanning оповещений в репозиториях. Дополнительные сведения см. в разделе О файлах SARIF для сканирования кода.

В этой статье выделяются ключевые свойства файлов SARIF, которые помогут вам загружать файлы, соответствующие требованиям GitHub и которые могут быть преобразованы в полезные оповещения.

Если вы используете GitHub Actions с переменными данных.code-scanning.codeql_workflow %} или с помощью CodeQL CLI, то результаты code scanning будут автоматически использовать поддерживаемую подмножество SARIF 2.1.0.

Данные для предотвращения дублирования оповещений

При каждой отправке результатов новой проверки кода эти результаты обрабатываются, а в репозиторий добавляются предупреждения. Чтобы предотвратить дублирование оповещений для одной и той же проблемы, code scanning использует отпечатки, позволяющие сопоставлять результаты различных запусков. Поэтому оповещения появляются только один раз в последнем запуске выбранной ветви. Это позволяет сопоставлять оповещения с правильной строкой кода при правке файлов. Значение ruleId для результата должно быть одинаковым для анализа.

Согласованные пути файлов

Путь к файлу должен быть одинаковым во всех запусках, чтобы обеспечить вычисление стабильного отпечатка пальца. Если в одном результате путь к файлу отличается, будет создаваться новый анализ и новое оповещение, а старое будет закрываться. Это приведет к созданию нескольких оповещений для одного результата.

Генерация отпечатков пальцев

GitHub использует свойство partialFingerprints в стандарте OASIS, чтобы определить логическую идентичность двух результатов. Дополнительные сведения см . в записи свойств partialFingerprints в документации ПО OASIS.

ФАЙЛЫ SARIF, созданные Рабочий процесс анализа CodeQL, или с помощью CodeQL CLI включают данные отпечатков пальцев. Если вы отправили файл SARIF с помощью действия upload-sarif, но эти данные отсутствуют, GitHub пытается заполнить поле partialFingerprints из исходных файлов. Дополнительные сведения о отправке результатов см. в разделе Отправка файла SARIF в GitHub.

Если вы отправляете файл SARIF без данных отпечатков с помощью конечной точки API /code-scanning/sarifs, оповещения code scanning будут обрабатываться и отображаться, но пользователи могут видеть повторяющиеся оповещения. Чтобы избежать появления повторяющихся оповещений, следует вычислить данные отпечатка и заполнить свойство partialFingerprints перед отправкой файла SARIF. Вы можете найти скрипт, в котором действие upload-sarif использует полезную отправную точку: https://github.com/github/codeql-action/blob/main/src/fingerprints.ts. Дополнительные сведения об API см. в разделе Конечные точки REST API для сканирования кода.

Правила и результаты

Файлы SARIF поддерживают как правила, так и результаты. Информация, хранящаяся в этих элементах, аналогична, но предназначена для разных целей.

  • Правила — это массив объектов reportingDescriptor, включенных в объект toolComponent. Здесь хранятся сведения о правилах, выполняемых во время анализа. Информация в этих объектах должна изменяться редко, обычно при обновлении средства.
  • Результаты хранятся в виде ряда объектов result в results в объекте run. Каждый объект result содержит сведения об одном оповещении в базе кода. В объекте results можно ссылаться на правило, которое обнаружило оповещение.

Сравнивая файлы SARIF, созданные путем анализа разных баз кода, с одним и тем же средством и правилами, вы должны увидеть различия в результатах анализа, но не в правилах.

Расположение исходных файлов

Указание расположений и строк исходного файла гарантирует отображение оповещений сканирования кода точно в файле, содержав обнаруженную проблему, что позволяет разрешить решение целевой проблемы.

Эта точность повышает эффективность процессов проверки и разрешения кода, упрощая рабочие процессы разработки, позволяя разработчикам решать проблемы непосредственно в контексте их базы кода.

Code scanning также отображает оповещения в результатах проверки запроса на вытягивание, если все строки кода, идентифицированные оповещением, существуют в диффе запроса на вытягивание.

Для отображения в проверке запроса на вытягивание оповещение должно соответствовать всем следующим условиям:

  • Все строки кода, определяемые оповещением, существуют в диффе запроса на вытягивание, включая первую строку оповещения.
  • Оповещение должно существовать в строках кода, добавленных или измененных в запросе на вытягивание, а не в строках, которые были удалены.

Объект physicalLocation в отправленном ФАЙЛЕ SARIF определяет строки кода для оповещения. Дополнительные сведения см. в разделе physicalLocation "Объект".

Корневой адрес исходных файлов

Code scanning интерпретирует результаты, сообщаемые с относительными путями относительно корня проанализированного репозитория. Если результат содержит абсолютный URI, URI преобразуется в относительный URI. Затем относительный URI можно сопоставить с файлом, зафиксированным в репозитории.

Исходный корень для преобразования из абсолютного в относительный URI можно указать одним из следующих способов.

  • Входные данные checkout_path для действия github/codeql-action/analyze.
  • Параметр checkout_uri для конечной точки API отправки SARIF. Дополнительные сведения см. в разделе Конечные точки REST API для сканирования кода.
  •         [
            `invocations[0].workingDirectory.uri`
            ](https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#_Toc9244365) свойство в объекте `run` в ФАЙЛЕ SARIF
    

Если указать корень источника, любое расположение артефакта, указанное с помощью абсолютного универсального кода ресурса (URI), должно использовать ту же схему URI. Если между схемой URI источника и одним или несколькими абсолютными URI существует несоответствие, отправка отклоняется.

Например, SARIF-файл отправляется с помощью исходного корневого каталога file:///github/workspace.

# Conversion of absolute URIs to relative URIs for location artifacts

file:///github/workspace/src/main.go -> src/main.go
file:///tmp/go-build/tmp.go          -> file:///tmp/go-build/tmp.go

Файл успешно отправлен, так как оба абсолютных URI используют ту же схему URI, что и исходный корневой каталог.

Если относительный универсальный код ресурса (URI) для результата сопоставляется с файлом, определенным с помощью асимметричной связи, сканирование кода не может отобразить результат. Таким образом, необходимо разрешить все симлинированные файлы и сообщить о любых результатах в этих файлах с помощью разрешенного URI.

Совместимость файлов

Вы можете проверить, совместим ли ФАЙЛ SARIF с code scanning, проверив его с помощью правил приема GitHub . Дополнительные сведения см. на странице проверяющего элемента управления SARIF Microsoft.

Для каждого сжатых gzip-файла SARIF отправка SARIF поддерживает максимальный размер 10 МБ. Любые отправки свыше этого ограничения будут отклонены. Если файл SARIF слишком большой, так как он содержит слишком много результатов, необходимо обновить конфигурацию, чтобы сосредоточиться на результатах для наиболее важных правил или запросов. Дополнительные сведения см. в разделе Файл результатов SARIF слишком велик.

Code scanning поддерживает отправку максимального количества записей для объектов данных в следующей таблице. Если любой из этих объектов превышает максимальное значение, то файл SARIF отклоняется. Для некоторых объектов также существует дополнительное ограничение на количество отображаемых значений. По возможности отображаются наиболее важные значения. Чтобы получить большую часть анализа при добавлении данных выше поддерживаемых ограничений, попробуйте оптимизировать конфигурацию анализа (например, для средства CodeQL и определить и отключить самые шумные запросы. Дополнительные сведения см. в разделе Результаты SARIF превышают одно или несколько ограничений.

Данные SARIFМаксимальные значенияОграничения усечения данных
Выполняется для каждого файла20нет
Результаты на выполнение25,000Будут включены только первые 5000 результатов, приоритетные по серьезности.
Правила для каждого запуска25,000нет
Расширения инструментов для каждого запуска100нет
Расположения потоков на результат10,000Будут включены только первые 1000 расположений потоков, используя приоритет.
Расположение на результат1,000Будут включены только 100 расположений.
Теги для каждого правила20Будут включены только 10 тегов.
Ограничение оповещений1 000 000нет

Для информации о других ошибках см. АВТОЗАГОЛОВОК.

Поддерживаемые свойства

При использовании подсистемы анализа кода, отличной от CodeQL, можно проверить поддерживаемые свойства SARIF, чтобы оптимизировать способ отображения результатов анализа в GitHub.

Примечание.

Необходимо указать явное значение для любого свойства, помеченного как обязательное. Пустая строка не поддерживается для обязательных свойств.

Любой допустимый выходной файл SARIF 2.1.0 можно отправить, но code scanning будет использовать только указанные ниже поддерживаемые свойства.

объект sarifLog

Имя.ОбязательноDescription
$schemaУниверсальный код ресурса (URI) схемы JSON для SARIF версии 2.1.0. Например: https://json.schemastore.org/sarif-2.1.0.json.
versionCode scanning поддерживает только версию 2.1.0SARIF.
runs[]Файл SARIF содержит массив из одного или нескольких запусков. Каждый запуск представляет собой один запуск средства анализа. Дополнительные сведения о run см. в разделе Объект run.

объект run

Code scanning использует run объект для фильтрации результатов по инструменту и предоставления сведений об источнике результата. Объект run содержит объект компонента средства tool.driver, содержащий сведения о средстве, которое создало результаты. Каждый объект run может содержать только результаты для одного средства анализа.

Имя.ОбязательноDescription
tool.driverОбъект toolComponent, описывающий средство анализа. Дополнительные сведения см. в разделе Объект toolComponent.
tool.extensions[]Массив объектов toolComponent, представляющих все подключаемые модули или расширения, используемые средством во время анализа. Дополнительные сведения см. в разделе Объект toolComponent.
invocation.workingDirectory.uriЭто поле используется только в том случае, если checkout_uri (только API отправки SARIF) или checkout_path (только GitHub Actions не предоставляются. Значение используется для преобразования абсолютных URI, используемых в объектах physicalLocation в относительные URI. Дополнительные сведения см. в разделе "Указание корневого каталога" для исходных файлов.
results[]Результаты средства анализа. Code scanning отображает результаты на GitHub. Дополнительные сведения см. в разделе Объект result.

объект toolComponent

Имя.ОбязательноDescription
nameИмя средства анализа. Code scanning отображает имя GitHub для фильтрации результатов по инструменту.
versionВерсия средства анализа. Code scanning использует номер версии для отслеживания изменений результатов из-за изменения версии средства, а не изменения в анализируемом коде. Если файл SARIF содержит поле semanticVersion, code scanning не использует version.
semanticVersionВерсия средства анализа, заданная форматом Семантического версионирования 2.0. Code scanning использует номер версии для отслеживания изменений результатов из-за изменения версии средства, а не изменения в анализируемом коде. Если файл SARIF содержит поле semanticVersion, code scanning не использует version. Дополнительные сведения см. в документации по семантической версии версии 2.0.0 .
rules[]Массив объектов reportingDescriptor, представляющих правила. Средство анализа использует правила для поиска проблем в анализируемом коде. Дополнительные сведения см. в разделе Объект reportingDescriptor.

объект reportingDescriptor

Здесь хранятся сведения о правилах, выполняемых во время анализа. Информация в этих объектах должна изменяться редко, обычно при обновлении средства. Для получения дополнительной информации см. Правила и результаты выше.

Имя.ОбязательноDescription
idУникальный идентификатор правила.
          `id` ссылается на другие части файла SARIF, а code scanning может его использовать для отображения URL-адресов в GitHub. |

| name | | Имя правила. Code scanning отображает имя, позволяющее фильтровать результаты по правилу на GitHub. Ограничено 255 символами. | | shortDescription.text | | Краткое описание правила. Code scanning отображает краткое описание для GitHub рядом с соответствующими результатами. Ограничено 1024 символами. | fullDescription.text | | Описание правила. Code scanning отображает полное описание GitHub рядом с соответствующими результатами. Ограничено 1024 символами. | defaultConfiguration.level | | Уровень серьезности правила по умолчанию. Code scanning использует уровни серьезности, чтобы понять, насколько важен результат для данного правила. По умолчанию defaultConfiguration.level задано значение warning. Однако можно переопределить уровень по умолчанию для правила, задав level атрибут в объекте, связанном result с результатом. Дополнительные сведения см. в документации по объектуresult. Допустимые значенияdefaultConfiguration.level: note``warning и error. | help.text | | Документация по правилу с использованием текстового формата. Code scanning отображает эту справочную документацию рядом с соответствующими результатами. | help.markdown | | (Рекомендуется) Документация по правилу с использованием формата Markdown. Code scanning отображает эту справочную документацию рядом с соответствующими результатами. Если объект help.markdown доступен, то отображается вместо help.text. | properties.tags[] | | Массив строк. Code scanning используется tags для фильтрации результатов на GitHub. Например, можно отфильтровать все результаты с тегом security. | properties.precision | | (Рекомендуется) Строка, указывающая, насколько часто результаты, указанные этим правилом, являются истинными. Например, если правило имеет известный высокий ложноположительный коэффициент, то точность должна быть low. Code scanning упорядочивает результаты по точности GitHub таким образом, чтобы результаты с наибольшим levelзначением отображались precision сначала. Это может быть very-high, high, medium или low. | properties.problem.severity | | (Рекомендуется) Строка, указывающая уровень серьезности любых оповещений, создаваемых запросом, небезопасным. Со свойством properties.precision определяет, отображаются ли результаты по умолчанию в GitHub так, чтобы сначала отображались результаты с наивысшим значением problem.severity и наибольшей precision. Это может быть error, warning или recommendation. | properties.security-severity | | (Рекомендуется только для правил безопасности) Если вы включаете значение для этого поля, результаты правила рассматриваются как результаты безопасности. Строка, представляющая оценку, которая указывает уровень серьезности, значение, превышающее 0,0 до 10,0, для запросов безопасности (@tags включая security). Со свойством properties.precision определяет, отображаются ли результаты по умолчанию в GitHub так, чтобы сначала отображались результаты с наивысшим значением security-severity и наибольшей precision. Code scanning преобразует числовые оценки следующим образом: более 9,0 — criticalот 7,0 до 8,9 high, от 4,0 до 6,9 — medium от 0,1 до 3,9 low. Значение 0.0 или любое другое значение за пределами заданного диапазона считается отсутствием серьезности безопасности.

объект result

Каждый объект result содержит сведения об одном оповещении в базе кода. В объекте results можно ссылаться на правило, которое обнаружило оповещение. Для получения дополнительной информации см. Правила и результаты выше.

Имя.ОбязательноDescription
ruleIdУникальный идентификатор правила (reportingDescriptor.id). Дополнительные сведения см. в разделе Объект reportingDescriptor. Code scanning использует идентификатор правила для фильтрации результатов по правилу на GitHub.
ruleIndexИндекс связанного правила (объект reportingDescriptor) в массиве rules компонента средства. Дополнительные сведения см. в разделе Объект run. Допустимый диапазон для этого свойства от 0 до 2^63 – 1.
ruleСсылка, используемая для поиска правила (дескриптора отчетности) для этого результата. Дополнительные сведения см. в разделе Объект reportingDescriptor.
levelСерьезность результата. Этот уровень переопределяет серьезность по умолчанию, определенную правилом. Code scanning использует уровень для фильтрации результатов по уровню серьезности на GitHub.
message.textСообщение, которое описывает результат. Code scanning отображает текст сообщения в качестве заголовка результата. Если видимое пространство ограничено, отображается только первое предложение сообщения.
locations[]Набор расположений, в которых был обнаружен результат ( не более 10). Следует включить только одно расположение, если проблему не получается исправить только внесением изменений в каждом указанном расположении.
          **Примечание.** Для отображения результата требуется по крайней мере одно расположение для code scanning. Code scanning будет использовать это свойство, чтобы решить, какой файл следует пользоваться результатом. Используется только первое значение этого массива. Все остальные значения не учитываются.

| partialFingerprints| | Набор строк, используемых для отслеживания уникального идентификатора результата. Code scanning используется partialFingerprints для точного определения результатов в фиксациях и ветвях. Code scanning попытается использовать partialFingerprints , если они существуют. Если вы отправляете сторонние файлы SARIF с помощью upload-action, то для вас действие создаст partialFingerprints (если их нет в файле SARIF). Дополнительные сведения см. в разделе "Предоставление данных для отслеживания оповещений сканирования кода в разных запусках". Примечание. Code scanning использует только те primaryLocationLineHashданные. | codeFlows[].threadFlows[].locations[]| | Массив объектов location для объекта threadFlow, который описывает ход выполнения программы через поток выполнения. Объект codeFlow описывает шаблон выполнения кода для обнаружения результата. Если потоки кода предоставлены, code scanning развернет потоки кода в GitHub для соответствующего результата. Дополнительные сведения см. в разделе Объект location. | relatedLocations[]| | Набор расположений, относящихся к этому результату. Code scanning будет связываться со связанными расположениями, когда они внедрены в результирующий сообщение. Дополнительные сведения см. в разделе Объект location.

объект location

Расположение в артефакте программирования, например файл в репозитории или файл, созданный во время сборки.

Имя.ОбязательноDescription
location.idУникальный идентификатор, отличающий это расположение от всех остальных расположений в одном объекте результата. Допустимый диапазон для этого свойства от 0 до 2^63 – 1.
location.physicalLocationИдентифицирует артефакт и регион. Более подробную информацию см. в разделе physicalLocation.
location.message.textСообщение, соответствующее расположению.

объект physicalLocation

Имя.ОбязательноDescription
artifactLocation.uriУниверсальный код ресурса (URI), указывающий расположение артефакта, обычно это файл в репозитории или созданный во время сборки. Для получения лучших результатов мы рекомендуем использовать относительный путь от корня анализируемого репозитория GitHub. Например: src/main.js. Дополнительные сведения о URI артефактов см. в разделе "Указание корневого каталога" для исходных файлов.
region.startLineНомер строки первого символа в регионе.
region.startColumnНомер столбца первого символа в регионе.
region.endLineНомер строки последнего символа в регионе.
region.endColumnНомер столбца символа, следующего за концом региона.

объект runAutomationDetails

Объект runAutomationDetails содержит сведения, указывающие идентификатор выполнения.

Имя.ОбязательноDescription
idСтрока, идентифицирующая категорию анализа и идентификатор выполнения. Используйте, если вы хотите передать несколько файлов SARIF для одного и того же средства и фиксации, но для разных языков или различных частей кода.

Использование объекта runAutomationDetails не является обязательным.

Поле id может включать категорию анализа и идентификатор выполнения. Мы не используем элемент идентификатора выполнения в поле id, но сохраняем его.

Используйте эту категорию для различения нескольких анализов для одного и того же средства или фиксации, но для разных языков или различных элементов кода. Используйте идентификатор выполнения, чтобы определить конкретный запуск анализа, например дату запуска анализа.

          `id` интерпретируется как `category/run-id`. Если `id` содержит косую черту (`/`), то вся строка является `run_id`, а `category` будет пустой. В противном случае `category` является все, что находится в строке до последней косой черты, а все, что после нее — `run_id`.
idкатегорияrun_id
my-analysis/tool1/2022-01-02my-analysis/tool12022-01-02
my-analysis/tool1/my-analysis/tool1None
MY-анализ для Tool1NoneMY-анализ для Tool1
  • Выполнение с id для my-analysis/tool1/2021-02-01 относится к категории my-analysis/tool1.
  • Выполнение с id для my-analysis/tool1/ относится к категории my-analysis/tool1, но не отличается от других выполнений в этой категории.
  • Выполнение, id которого является my-analysis for tool1, имеет уникальный идентификатор, но не может быть выводимым для принадлежности к какой-либо категории.

Дополнительные сведения об объекте runAutomationDetails и поле id см. в разделе Объект runAutomationDetails в документации по OASIS.

Обратите внимание, что остальные поддерживаемые поля не учитываются.

Примеры выходных файлов SARIF

В этих примерах выходных файлов SARIF отображаются поддерживаемые свойства и примеры значений.

Пример с минимальными обязательными свойствами

Этот выходной файл SARIF содержит примеры значений для отображения минимальных обязательных свойств для результатов code scanning для нормальной работы. Если удалить какое-либо свойство, не включить значения или использовать пустую строку, эти данные не будут правильно отображаться или синхронизироваться с GitHub.

{
  "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
  "version": "2.1.0",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "Tool Name",
          "rules": [
            {
              "id": "R01"
                      ...
              "properties" : {
                 "id" : "java/unsafe-deserialization",
                 "kind" : "path-problem",
                 "name" : "...",
                 "problem.severity" : "error",
                 "security-severity" : "9.8",
               }
            }
          ]
        }
      },
      "results": [
        {
          "ruleId": "R01",
          "message": {
            "text": "Result text. This result does not have a rule associated."
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "fileURI"
                },
                "region": {
                  "startLine": 2,
                  "startColumn": 7,
                  "endColumn": 10
                }
              }
            }
          ],
          "partialFingerprints": {
            "primaryLocationLineHash": "39fa2ee980eb94b0:1"
          }
        }
      ]
    }
  ]
}

Руководство по относительному URI для производителей SARIF

Этот выходной файл SARIF содержит пример значений для поля originalUriBaseIds, показывающий минимальные обязательные свойства, которые производитель SARIF должен включать при использовании относительных ссылок URI.

Примечание.

Хотя это свойство не требуется GitHub для результатов code scanning для правильного отображения результатов, необходимо создать допустимые выходные данные SARIF при использовании относительных ссылок URI.

{
  "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
  "version": "2.1.0",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "Tool Name",
          "rules": [
            {
              "id": "R01"
                      ...
              "properties" : {
                 "id" : "java/unsafe-deserialization",
                 "kind" : "path-problem",
                 "name" : "...",
                 "problem.severity" : "error",
                 "security-severity" : "9.8",
               }
            }
          ]
        }
      },
      "originalUriBaseIds": {
        "PROJECTROOT": {
         "uri": "file:///C:/Users/Mary/code/TheProject/",
           "description": {
             "text": "The root directory for all project files."
           }
        },
         "%SRCROOT%": {
           "uri": "src/",
           "uriBaseId": "PROJECTROOT",
           "description": {
             "text": "The root of the source tree."
           }
         }
      },
      "results": [
        {
          "ruleId": "R01",
          "message": {
            "text": "Result text. This result does not have a rule associated."
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "fileURI",
                  "uriBaseId": "%SRCROOT%"
                },
                "region": {
                  "startLine": 2,
                  "startColumn": 7,
                  "endColumn": 10
                }
              }
            }
          ],
          "partialFingerprints": {
            "primaryLocationLineHash": "39fa2ee980eb94b0:1"
          }
        }
      ]
    }
  ]
}

Пример, показывающий все поддерживаемые свойства SARIF

Этот выходной файл SARIF содержит примеры значений для отображения всех поддерживаемых свойств SARIF для code scanning.

{
  "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
  "version": "2.1.0",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "Tool Name",
          "semanticVersion": "2.0.0",
          "rules": [
            {
              "id": "3f292041e51d22005ce48f39df3585d44ce1b0ad",
              "name": "js/unused-local-variable",
              "shortDescription": {
                "text": "Unused variable, import, function or class"
              },
              "fullDescription": {
                "text": "Unused variables, imports, functions or classes may be a symptom of a bug and should be examined carefully."
              },
              "defaultConfiguration": {
                "level": "note"
              },
              "properties": {
                "tags": [
                  "maintainability"
                ],
                "precision": "very-high"
              }
            },
            {
              "id": "d5b664aefd5ca4b21b52fdc1d744d7d6ab6886d0",
              "name": "js/inconsistent-use-of-new",
              "shortDescription": {
                "text": "Inconsistent use of 'new'"
              },
              "fullDescription": {
                "text": "If a function is intended to be a constructor, it should always be invoked with 'new'. Otherwise, it should always be invoked as a normal function, that is, without 'new'."
              },
              "properties": {
                "tags": [
                  "reliability",
                  "correctness",
                  "language-features"
                ],
                "precision": "very-high"
              }
            },
            {
              "id": "R01"
            }
          ]
        }
      },
      "automationDetails": {
        "id": "my-category/"
      },
      "results": [
        {
          "ruleId": "3f292041e51d22005ce48f39df3585d44ce1b0ad",
          "ruleIndex": 0,
          "message": {
            "text": "Unused variable foo."
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "main.js",
                  "uriBaseId": "%SRCROOT%"
                },
                "region": {
                  "startLine": 2,
                  "startColumn": 7,
                  "endColumn": 10
                }
              }
            }
          ],
          "partialFingerprints": {
            "primaryLocationLineHash": "39fa2ee980eb94b0:1",
            "primaryLocationStartColumnFingerprint": "4"
          }
        },
        {
          "ruleId": "d5b664aefd5ca4b21b52fdc1d744d7d6ab6886d0",
          "ruleIndex": 1,
          "message": {
            "text": "Function resolvingPromise is sometimes invoked as a constructor (for example [here](1)), and sometimes as a normal function (for example [here](2))."
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "src/promises.js",
                  "uriBaseId": "%SRCROOT%"
                },
                "region": {
                  "startLine": 2
                }
              }
            }
          ],
          "partialFingerprints": {
            "primaryLocationLineHash": "5061c3315a741b7d:1",
            "primaryLocationStartColumnFingerprint": "7"
          },
          "relatedLocations": [
            {
              "id": 1,
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "src/ParseObject.js",
                  "uriBaseId": "%SRCROOT%"
                },
                "region": {
                  "startLine": 2281,
                  "startColumn": 33,
                  "endColumn": 55
                }
              },
              "message": {
                "text": "here"
              }
            },
            {
              "id": 2,
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "src/LiveQueryClient.js",
                  "uriBaseId": "%SRCROOT%"
                },
                "region": {
                  "startLine": 166
                }
              },
              "message": {
                "text": "here"
              }
            }
          ]
        },
        {
          "ruleId": "R01",
          "message": {
            "text": "Specifying both [ruleIndex](1) and [ruleId](2) might lead to inconsistencies."
          },
          "level": "error",
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "full.sarif",
                  "uriBaseId": "%SRCROOT%"
                },
                "region": {
                  "startLine": 54,
                  "startColumn": 10,
                  "endLine": 55,
                  "endColumn": 25
                }
              }
            }
          ],
          "relatedLocations": [
            {
              "id": 1,
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "full.sarif"
                },
                "region": {
                  "startLine": 81,
                  "startColumn": 10,
                  "endColumn": 18
                }
              },
              "message": {
                "text": "here"
              }
            },
            {
              "id": 2,
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "full.sarif"
                },
                "region": {
                  "startLine": 82,
                  "startColumn": 10,
                  "endColumn": 21
                }
              },
              "message": {
                "text": "here"
              }
            }
          ],
          "codeFlows": [
            {
              "threadFlows": [
                {
                  "locations": [
                    {
                      "location": {
                        "physicalLocation": {
                          "region": {
                            "startLine": 11,
                            "endLine": 29,
                            "startColumn": 10,
                            "endColumn": 18
                          },
                          "artifactLocation": {
                            "uriBaseId": "%SRCROOT%",
                            "uri": "full.sarif"
                          }
                        },
                        "message": {
                          "text": "Rule has index 0"
                        }
                      }
                    },
                    {
                      "location": {
                        "physicalLocation": {
                          "region": {
                            "endColumn": 47,
                            "startColumn": 12,
                            "startLine": 12
                          },
                          "artifactLocation": {
                            "uriBaseId": "%SRCROOT%",
                            "uri": "full.sarif"
                          }
                        }
                      }
                    }
                  ]
                }
              ]
            }
          ],
          "partialFingerprints": {
            "primaryLocationLineHash": "ABC:2"
          }
        }
      ],
      "columnKind": "utf16CodeUnits"
    }
  ]
}