PHP_CodeSniffer icon indicating copy to clipboard operation
PHP_CodeSniffer copied to clipboard

Feature request: SARIF Output

Open nvuillam opened this issue 2 years ago • 15 comments

Hi, is it in the roadmap to make PHP_CodeSniffer provide SARIF output ? (SARIF is the OASIS common format for all analysis tools )

It would help improve its integration within MegaLinter :)

Best regards

nvuillam avatar Dec 08 '21 18:12 nvuillam

No, it's not on the roadmap. I hadn't heard of the format before and I couldn't devote any time to this unless the demand was high. I also wouldn't accept a PR to add the report format to the core unless the demand was there.

gsherwood avatar Dec 08 '21 21:12 gsherwood

The format is widely used, especially since GitHub reads it to display lint errors directly in Pull Requests UI, so if a good samaritan would submit a PR, you may be wise to accept it :)

As you can see in this table (orange badges), the format is more and more used by many code analyzers, including PHP ones like PHP_Stan ^^

https://megalinter.github.io/v6-alpha/supported-linters/

nvuillam avatar Dec 08 '21 21:12 nvuillam

@nvuillam Just FYI: external repositories/standards can use their own reports.

For more information on how to do so, see these issues/PRs:

  • https://github.com/squizlabs/PHP_CodeSniffer/issues/1942
  • https://github.com/squizlabs/PHP_CodeSniffer/pull/1948

jrfnl avatar Dec 09 '21 22:12 jrfnl

Also just looked at the specs and ... jeezs louise.... who's got the time to even read that, let alone implement it ? This would require a study of that standard which could easily take several weeks by the looks of it and does not align with any of the other report formats provided. This won't be straight-forward to implement.

jrfnl avatar Dec 09 '21 22:12 jrfnl

The specs are long, but I think a first version with tool and results should not be so hard :) (and also not sure anyone has read them, people just follow the examples :D )

Example of SARIF output from python bandit (snippets are not mandatory i think -most of other SARIF output linters do not have it- , just message, artifactLocation and region )

Building such format with only the information you currently should be reasonable ^^

{
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "Bandit",
          "rules": [
            {
              "id": "B404",
              "name": "blacklist",
              "helpUri": "https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b404-import-subprocess"
            },
            {
              "id": "B506",
              "name": "yaml_load",
              "helpUri": "https://bandit.readthedocs.io/en/latest/plugins/b506_yaml_load.html"
            },
            {
              "id": "B602",
              "name": "subprocess_popen_with_shell_equals_true",
              "helpUri": "https://bandit.readthedocs.io/en/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html"
            },
            {
              "id": "B607",
              "name": "start_process_with_partial_path",
              "helpUri": "https://bandit.readthedocs.io/en/latest/plugins/b607_start_process_with_partial_path.html"
            },
            {
              "id": "B110",
              "name": "try_except_pass",
              "helpUri": "https://bandit.readthedocs.io/en/latest/plugins/b110_try_except_pass.html"
            }
          ]
        }
      },
      "invocations": [
        {
          "executionSuccessful": true,
          "endTimeUtc": "2021-12-09T17:03:57Z"
        }
      ],
      "results": [
        {
          "message": {
            "text": "Consider possible security implications associated with the subprocess module."
          },
          "level": "note",
          "locations": [
            {
              "physicalLocation": {
                "region": {
                  "snippet": {
                    "text": "import subprocess\n"
                  },
                  "startLine": 10
                },
                "artifactLocation": {
                  "uri": "file:///github/workspace/.github/workflows/build.py"
                },
                "contextRegion": {
                  "snippet": {
                    "text": "import re\nimport subprocess\nimport sys\n"
                  },
                  "endLine": 11,
                  "startLine": 9
                }
              }
            }
          ],
          "properties": {
            "issue_confidence": "HIGH",
            "issue_severity": "LOW"
          },
          "ruleId": "B404",
          "ruleIndex": 0
        },
        {
          "message": {
            "text": "Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load()."
          },
          "locations": [
            {
              "physicalLocation": {
                "region": {
                  "snippet": {
                    "text": "            descriptor = yaml.load(f, Loader=yaml.FullLoader)\n"
                  },
                  "startLine": 121
                },
                "artifactLocation": {
                  "uri": "file:///github/workspace/.github/workflows/build.py"
                },
                "contextRegion": {
                  "snippet": {
                    "text": "        with open(descriptor_file, \"r\", encoding=\"utf-8\") as f:\n            descriptor = yaml.load(f, Loader=yaml.FullLoader)\n            if (\n"
                  },
                  "endLine": 122,
                  "startLine": 120
                }
              }
            }
          ],
          "properties": {
            "issue_confidence": "HIGH",
            "issue_severity": "MEDIUM"
          },
          "ruleId": "B506",
          "ruleIndex": 1
        },
        {
          "message": {
            "text": "Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load()."
          },
          "locations": [
            {
              "physicalLocation": {
                "region": {
                  "snippet": {
                    "text": "                instance=yaml.load(mega_linter_config, Loader=yaml.FullLoader),\n"
                  },
                  "startLine": 1732
                },
                "artifactLocation": {
                  "uri": "file:///github/workspace/.github/workflows/build.py"
                },
                "contextRegion": {
                  "snippet": {
                    "text": "            jsonschema.validate(\n                instance=yaml.load(mega_linter_config, Loader=yaml.FullLoader),\n                schema=yaml.load(descriptor_schema, Loader=yaml.FullLoader),\n"
                  },
                  "endLine": 1733,
                  "startLine": 1731
                }
              }
            }
          ],
          "properties": {
            "issue_confidence": "HIGH",
            "issue_severity": "MEDIUM"
          },
          "ruleId": "B506",
          "ruleIndex": 1
        },
        {
          "message": {
            "text": "Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load()."
          },
          "locations": [
            {
              "physicalLocation": {
                "region": {
                  "snippet": {
                    "text": "                schema=yaml.load(descriptor_schema, Loader=yaml.FullLoader),\n"
                  },
                  "startLine": 1733
                },
                "artifactLocation": {
                  "uri": "file:///github/workspace/.github/workflows/build.py"
                },
                "contextRegion": {
                  "snippet": {
                    "text": "                instance=yaml.load(mega_linter_config, Loader=yaml.FullLoader),\n                schema=yaml.load(descriptor_schema, Loader=yaml.FullLoader),\n            )\n"
                  },
                  "endLine": 1734,
                  "startLine": 1732
                }
              }
            }
          ],
          "properties": {
            "issue_confidence": "HIGH",
            "issue_severity": "MEDIUM"
          },
          "ruleId": "B506",
          "ruleIndex": 1
        },
        {
          "message": {
            "text": "Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load()."
          },
          "locations": [
            {
              "physicalLocation": {
                "region": {
                  "snippet": {
                    "text": "                        instance=yaml.load(descriptor, Loader=yaml.FullLoader),\n"
                  },
                  "startLine": 1749
                },
                "artifactLocation": {
                  "uri": "file:///github/workspace/.github/workflows/build.py"
                },
                "contextRegion": {
                  "snippet": {
                    "text": "                    jsonschema.validate(\n                        instance=yaml.load(descriptor, Loader=yaml.FullLoader),\n                        schema=yaml.load(descriptor_schema, Loader=yaml.FullLoader),\n"
                  },
                  "endLine": 1750,
                  "startLine": 1748
                }
              }
            }
          ],
          "properties": {
            "issue_confidence": "HIGH",
            "issue_severity": "MEDIUM"
          },
          "ruleId": "B506",
          "ruleIndex": 1
        },
        {
          "message": {
            "text": "Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load()."
          },
          "locations": [
            {
              "physicalLocation": {
                "region": {
                  "snippet": {
                    "text": "                        schema=yaml.load(descriptor_schema, Loader=yaml.FullLoader),\n"
                  },
                  "startLine": 1750
                },
                "artifactLocation": {
                  "uri": "file:///github/workspace/.github/workflows/build.py"
                },
                "contextRegion": {
                  "snippet": {
                    "text": "                        instance=yaml.load(descriptor, Loader=yaml.FullLoader),\n                        schema=yaml.load(descriptor_schema, Loader=yaml.FullLoader),\n                    )\n"
                  },
                  "endLine": 1751,
                  "startLine": 1749
                }
              }
            }
          ],
          "properties": {
            "issue_confidence": "HIGH",
            "issue_severity": "MEDIUM"
          },
          "ruleId": "B506",
          "ruleIndex": 1
        },
        {
          "message": {
            "text": "subprocess call with shell=True identified, security issue."
          },
          "level": "error",
          "locations": [
            {
              "physicalLocation": {
                "region": {
                  "snippet": {
                    "text": "        shell=True,\n"
                  },
                  "startLine": 2220
                },
                "artifactLocation": {
                  "uri": "file:///github/workspace/.github/workflows/build.py"
                },
                "contextRegion": {
                  "snippet": {
                    "text": "        cwd=os.getcwd() + \"/.automation\",\n        shell=True,\n    )\n    print(process.stdout)\n    print(process.stderr)\n\n\ndef generate_version():\n"
                  },
                  "endLine": 2226,
                  "startLine": 2219
                }
              }
            }
          ],
          "properties": {
            "issue_confidence": "HIGH",
            "issue_severity": "HIGH"
          },
          "ruleId": "B602",
          "ruleIndex": 2
        },
        {
          "message": {
            "text": "Starting a process with a partial executable path"
          },
          "level": "note",
          "locations": [
            {
              "physicalLocation": {
                "region": {
                  "snippet": {
                    "text": "    process = subprocess.run(\n"
                  },
                  "startLine": 2230
                },
                "artifactLocation": {
                  "uri": "file:///github/workspace/.github/workflows/build.py"
                },
                "contextRegion": {
                  "snippet": {
                    "text": "    cwd_to_use = os.getcwd() + \"/mega-linter-runner\"\n    process = subprocess.run(\n        [\n            \"npm\",\n            \"version\",\n            \"--newversion\",\n            RELEASE_TAG,\n            \"-no-git-tag-version\",\n            \"--no-commit-hooks\",\n        ],\n        stdout=subprocess.PIPE,\n        universal_newlines=True,\n        cwd=cwd_to_use,\n        shell=True,\n    )\n"
                  },
                  "endLine": 2243,
                  "startLine": 2229
                }
              }
            }
          ],
          "properties": {
            "issue_confidence": "HIGH",
            "issue_severity": "LOW"
          },
          "ruleId": "B607",
          "ruleIndex": 3
        },
        {
          "message": {
            "text": "subprocess call with shell=True identified, security issue."
          },
          "level": "error",
          "locations": [
            {
              "physicalLocation": {
                "region": {
                  "snippet": {
                    "text": "        shell=True,\n"
                  },
                  "startLine": 2242
                },
                "artifactLocation": {
                  "uri": "file:///github/workspace/.github/workflows/build.py"
                },
                "contextRegion": {
                  "snippet": {
                    "text": "        cwd=cwd_to_use,\n        shell=True,\n    )\n    print(process.stdout)\n    print(process.stderr)\n    # Update changelog\n    changelog_file = f\"{REPO_HOME}/CHANGELOG.md\"\n\n    with open(changelog_file, \"r\", encoding=\"utf-8\") as md_file:\n        changelog_content = md_file.read()\n    changelog_content = changelog_content.replace(\"<!-- linter-versions-end -->\", \"\")\n    new_release_lines = [\n        \",\" \"<!-- unreleased-content-marker -->\",\n        \"\",\n        \"- Linter versions upgrades\",\n"
                  },
                  "endLine": 2255,
                  "startLine": 2241
                }
              }
            }
          ],
          "properties": {
            "issue_confidence": "HIGH",
            "issue_severity": "HIGH"
          },
          "ruleId": "B602",
          "ruleIndex": 2
        },
        {
          "message": {
            "text": "Try, Except, Pass detected."
          },
          "level": "note",
          "locations": [
            {
              "physicalLocation": {
                "region": {
                  "snippet": {
                    "text": "except:\n"
                  },
                  "startLine": 3
                },
                "artifactLocation": {
                  "uri": "file:///github/workspace/python_bad_1.py"
                },
                "contextRegion": {
                  "snippet": {
                    "text": "    pass\nexcept:\n    pass\n"
                  },
                  "endLine": 4,
                  "startLine": 2
                }
              }
            }
          ],
          "properties": {
            "issue_confidence": "HIGH",
            "issue_severity": "LOW"
          },
          "ruleId": "B110",
          "ruleIndex": 4
        }
      ]
    }
  ],
  "version": "2.1.0",
  "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json"
}

nvuillam avatar Dec 10 '21 01:12 nvuillam