Skip to content
Snippets Groups Projects
sarif-per-repo-endpoint.mdx 10.8 KiB
Newer Older
---
sidebar_position: 2
---
import { Tabs, Callout } from 'nextra/components'

# SARIF per Repository

<Callout type="info">
  `GET /api/v1/repositories/{repositoryUrl}/sarif[?timestamp=<unix_timestamp>]`
</Callout>

This endpoint gets parameterized with the repository url. The repository url is the url of the repository that should be scanned. 
The url should be in the following format: `https://gitlab.opencode.de/zendis-repo-scanner`. **The url must be url encoded.** 
(`https%3A%2F%2Fgitlab.opencode.de%2Fzendis-repo-scanner`)

Using this format already opens the possibility to scan repositories from other platforms like GitHub, Bitbucket, etc. Besides 
that, caching is simplified because the url is unique (instead of using the project id and a platform url as query string for 
example) and the url is human readable, at least after url decoding. This might improve debugging in the future.

The timestamp parameter is optional. If it is provided, the endpoint will return the scan result that is closest (but never in 
the future) to the given timestamp. If it is not provided, the endpoint will return the latest scan result.

The result is returned in [SARIF format](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning). 
**SARIF simplifies possible later integration with other tools. It is an open standard and widely supported.**

### Example Request

<Tabs items={['GO', 'JavaScript', 'curl']}>
    <Tabs.Tab>
        ```go filename="main.go" copy
        package main

        import (
            "fmt"
            "io"
            "net/http"
        )

        func main() {
            url := "https://scanner.zend.is/api/v1/repositories/https%3A%2F%2Fgitlab.opencode.de%2Fzendis-repo-scanner/sarif"
            resp, err := http.Get(url)
            if err != nil {
                fmt.Println("Error:", err)
                return
            }
            defer resp.Body.Close()

            body, _ := io.ReadAll(resp.Body)
            fmt.Println(string(body))
        }
        ```
    </Tabs.Tab>
    <Tabs.Tab>
        ```js filename="main.js" copy
        const fetch = require('node-fetch');

        const url = 'https://scanner.zend.is/api/v1/repositories/https%3A%2F%2Fgitlab.opencode.de%2Fzendis-repo-scanner/sarif';

        fetch(url)
        .then(response => response.json())
        .then(data => console.log(data))
        .catch(error => console.error('Error:', error));
        ```
    </Tabs.Tab>
    <Tabs.Tab>
        ```bash filename="cli" copy
        curl -X GET "https://scanner.zend.is/api/v1/repositories/https%3A%2F%2Fgitlab.opencode.de%2Fzendis-repo-scanner/sarif"
        ```
    </Tabs.Tab>
</Tabs>

### Example JSON Response

```json
{
  "$schema": "https://www.schemastore.org/schemas/json/sarif-2.1.0.json",
  "version": "2.1.0",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "zendis-repo-scanner",
          "version": "1.0.0",
          "informationUri": "https://gitlab.opencode.de/zendis-repo-scanner",
          "rules": [
            {
              "id": "ISSUE_REACTION_TIME",
              "name": "Issue Reaction Time",
              "fullDescription": {
                "text": "Measures the average time it takes to respond to reported issues."
              },
              "help": {
                "text": "The average issue reaction time is calculated by dividing the sum of the time it took to respond to all issues by the number of issues. The reaction time is so important because it shows how quickly the project team can respond to issues and bugs. The faster the reaction time, the more likely the project is to be maintained and improved."
              },
              "properties": {
                "unit": "days",
                "thresholds": [
                  { "min": 0, "max": 1, "status": "gold" },
                  { "min": 2, "max": 7, "status": "silver" },
                  { "min": 8, "max": 14, "status": "bronze" }
                  { "min": 15, "max": null, "status": "fail" }
                ],
              }
            },
            {
              "id": "BUS_FACTOR",
              "name": "Bus Factor",
              "fullDescription": {
                "text": "Calculates the bus factor of the project, indicating how many contributors must become unavailable to jeopardize the project."
              },
              "help": {
                "text": "The bus factor is a measure of the project's resilience to losing contributors. The higher the bus factor, the more contributors can become unavailable before the project is in danger. The bus factor is so important because it shows how many people are familiar with the project and can take over if necessary."
              },
              "properties": {
                "unit": "contributors",
                "thresholds": [
                  { "min": 4, "max": null, "status": "gold" },
                  { "min": 3, "max": 3, "status": "silver" },
                  { "min": 2, "max": 2, "status": "bronze" }
                  { "min": 0, "max": 1, "status": "fail" }
                ],
              }
            },
            {
              "id": "SECURITY_POLICY",
              "name": "Security Policy",
              "fullDescription": {
                "text": "Checks if the project has a security policy."
              },
              "help": {
                "text": "A security policy is a set of rules and practices that the project team follows to ensure the security of the project. The security policy is so important because it shows how seriously the project team takes security and how well they are prepared to handle security incidents."
              },
              "properties": {
                "type": "boolean",
                "positiveStatus": "gold",
                "negativeStatus": "fail"
              }
            }
          ]
        }
      },
      "properties": {
        "timestamp": 1630000000,
        "badges": [
          {
            "badgeUrl": "https://img.shields.io/badge/maintenance-silver-lightgrey.svg",
            "badgeId": "MAINTENANCE",
            "badgeStatus": "silver",
            "badgeInformationUri": "https://gitlab.opencode.de/zendis-repo-scanner/badges/maintenance.md",
            "badgeExplanation": {
              "criteria": [
                {
                  "description": "The average issue reaction time qualifies as silver (4 days, between 1 and 7 days).",
                  "ruleId": "ISSUE_REACTION_TIME",
                  "value": 4,
                  "status": "silver"
                },
                {
                  "description": "The bus factor qualifies as gold (3 contributors or more).",
                  "ruleId": "BUS_FACTOR",
                  "value": 3,
                  "status": "gold"
                },
              ],
              "conclusion": "Based on the evaluation of the above criteria, this project is classified as Silver level maintenance."
            }
          },
          {
            "badgeUrl": "https://img.shields.io/badge/security-fail-red.svg",
            "badgeId": "SECURITY",
            "badgeStatus": "fail",
            "badgeInformationUri": "https://gitlab.opencode.de/zendis-repo-scanner/badges/security.md",
            "badgeExplanation": {
              "criteria": [
                {
                  "description": "A security policy does not exist for this project.",
                  "ruleId": "SECURITY_POLICY",
                  "value": false,
                  "status": "fail"
                }
              ],
              "conclusion": "Based on the evaluation of the above criteria, this project is not granted a security badge."
            }
          }
        ]
      },
      "results": [
        {
          "ruleId": "ISSUE_REACTION_TIME",
          "level": "note",
          "message": {
            "text": "The average issue reaction time is 4 days, which qualifies as silver (between 1 and 7 days)."
          },
          "properties": {
            "value": 4,
            "status": "silver",
            "unit": "days"
          }
        },
        {
          "ruleId": "BUS_FACTOR",
          "level": "note",
          "message": {
            "text": "The bus factor is 3, which qualifies as gold (3 contributors or more)."
          },
          "properties": {
            "value": 4,
            "status": "gold",
            "unit": "contributors"
          }
        },
        {
          "ruleId": "SECURITY_POLICY",
          "level": "warning",
          "message": {
            "text": "A security policy does not exist for this project."
          },
          "properties": {
            "value": false,
            "status": "fail"
          }
        }
      ]
    }
  ]
}
```
### Response Explanation

This JSON response contains the following information:

- The tool that generated the report (which will probably always be the same string)
- The properties of the tool, such as the version and the informationUri, basically to comply with the SARIF standard.
- The rules that the tool uses to evaluate the repository
  - Rule Ids will be all uppercase and snake case (e.g. `ISSUE_REACTION_TIME`) and will be unique.
- The properties of the rules, such as thresholds and units - those are dynamically added into the json. The report should never be stored like this inside the database. Thresholds are subject to change due to optimization. The database stores normalized entries like: `BUS_FACTOR: 3` and `ISSUE_REACTION_TIME: 4`. Thus the badges can be changed by modifying the thresholds and applied to past reports as well.
- The results of the run, which contain the following elements:
    - ruleId: The rule that was evaluated
    - level: The level of the result (warning if the rule is not met, note if the rule is met (the threshold or state does not matter))
    - message: The message of the result
    - properties: The properties of the result, such as the value, status, range, and unit
- The properties of the run, such as badges, which are dynamically added into the json. They contain the following elements:
    - badgeUrl: The url to the badge image - will be a dynamically generated svg
    - badgeId: The title of the badge
    - badgeInformationUri: The url to additional documentation about that badge
    - badgeStatus: The level of the badge, which can be one of the following:
        - `gold`
        - `silver`
        - `bronze`
        - `fail`
        - `expired`
    - badgeExplanation: The explanation of the badge, which contains the following elements:
        - criteria: A list of criteria that were evaluated to determine the badge
          - description: A description of the criteria
          - ruleId: The rule that was evaluated
          - value: The value that was evaluated
          - status: The status of the evaluation
          - range: The range of the evaluation
        - conclusion: The conclusion of the badge. This is a summary of the criteria that were evaluated.

Consent

On this website, we use the web analytics service Matomo to analyze and review the use of our website. Through the collected statistics, we can improve our offerings and make them more appealing for you. Here, you can decide whether to allow us to process your data and set corresponding cookies for these purposes, in addition to technically necessary cookies. Further information on data protection—especially regarding "cookies" and "Matomo"—can be found in our privacy policy. You can withdraw your consent at any time.