sealesj 4220be6c72 Vulnerability Scanning on Third Party Deps (flutter/engine#36506)
* initial flatten deps scan

* move 3rd party scan to separate action

* allow fork to run

* install requests

* use packages

* pip install

* rename

* conditional vuln report

* trailing whitespace

* trailing whitespace

* detailed print

* add testing file

* add upload test sarif

* results sarif

* move sarif

* upload modified sarif

* test flow

* test with results.sarif

* formatting

* test naming convention

* description with text in artifactLocation

* don't use locations

* use template sarif

* just use template

* add one field mod

* add another field mod

* use actual osvReport

* add field

* add field

* test

* no information uri

* no information uri

* add name

* template NA data for results

* back to minimal template

* dynamic rules

* template update

* no results

* only use template

* test

* new test

* new test

* add back locations

* descriptive fields

* test

* use package name

* variable commit hash

* add chromium accessibility readme support

* use batch query test

* clean up

* use variables for sarif template

* initial automating ancestor commit

* allow for workflow on testing

* install gitpython in workflow

* wrap in try

* expand try

* check commit is not none

* quiet clone

* fix commit newline

* proper print for failed deps

* remove gitpython

* remove import

* fix origin source

* remove .dart from dep names

* update dep

* typo

* update

* clone into controlled name repo now

* fix github upstream clone url

* test CVE finding

* use templated rule and result

* typo

* remove test CVE

* add link straight to OSV DB

* comments

* use os mkdir

* check time of pinned commit

* quiet git

* print osv api query results if vulns found

* move upstream mapping into DEPS file

* add testing for DEPS file

* add khronos exception

* add basic ancestor commit test

* no vulns message

* do not produce empty sarif

* add yaml

* remove unused python dep

* no change?

* no more print, causing recipe issues

* string test

* string test

* no more fstrings

* convert to .format

* syntax

* remove unused dep

* test

* switch test script

* no encoding

* add back test

* typo

* remove scan flat deps tests again

* update

* fix tests

* typo

* newline

* use checkout dir

* prefix

* update to use prefix

* lint

* runhook attempt

* lint

* lint

* lint

* lint

* no license blurb

* cleanup

* enable for main

* do not raise error

* run on branch

* data indentation

* check file existence

* workflow updates

* add push for testing

* syntax

* workflow test

* test github action

* syntax

* allow empty report

* update cron

* pin hash

* newline

* sort by key with prefix omitted

* alphabetize, copyright header

* pylint tests

* lint

* lint

* trailing whitespace?

* lint

* update

* get error types

* allow test

* use output

* only main branch

* licenses check

* results.sarif

* revert

* license updates

* add upstream

* replace Requests library with urllib, remove pylint wrapper

* lint

* undo license

* clone test nit

* isinstance

* DEPS formatting

Co-authored-by: Zachary Anderson <zanderso@users.noreply.github.com>

* use subprocess.check_output

* lint

* lint

* review syntax from comments

* remove line

* more description in error

* lint

* fix checkout path

* remove duplicate eval

* lint

* lint

* lint

* clone-test mkdir and cleanup

* use shutil.rmtree for non-empty dir

* lint

* linting

* linting

* var name

* Update ci/deps_parser_tests.py

Co-authored-by: Zachary Anderson <zanderso@users.noreply.github.com>

* Update ci/deps_parser_tests.py

Co-authored-by: Zachary Anderson <zanderso@users.noreply.github.com>

* more description

* lint

* refactor deps file parsing

* early return

* lint

Co-authored-by: Zachary Anderson <zanderso@users.noreply.github.com>
2022-11-23 15:07:43 -05:00

127 lines
3.3 KiB
Python

#!/usr/bin/env python3
#
# Copyright 2013 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# Usage: deps_parser.py --deps <DEPS file> --output <flattened deps>
#
# This script parses the DEPS file, extracts the fully qualified dependencies
# and writes the to a file. This file will be later used to validate the dependencies
# are pinned to a hash.
import argparse
import os
import re
import sys
SCRIPT_DIR = os.path.dirname(sys.argv[0])
CHECKOUT_ROOT = os.path.realpath(os.path.join(SCRIPT_DIR, '..'))
CHROMIUM_README_FILE = 'third_party/accessibility/README.md'
CHROMIUM_README_COMMIT_LINE = 4 # the fifth line will always contain the commit hash
CHROMIUM = 'https://chromium.googlesource.com/chromium/src'
# Used in parsing the DEPS file.
class VarImpl:
_env_vars = {
'host_cpu': 'x64',
'host_os': 'linux',
}
def __init__(self, local_scope):
self._local_scope = local_scope
def lookup(self, var_name):
"""Implements the Var syntax."""
if var_name in self._local_scope.get('vars', {}):
return self._local_scope['vars'][var_name]
# Inject default values for env variables
if var_name in self._env_vars:
return self._env_vars[var_name]
raise Exception('Var is not defined: %s' % var_name)
def parse_deps_file(deps_file):
local_scope = {}
var = VarImpl(local_scope)
global_scope = {
'Var': var.lookup,
'deps_os': {},
}
# Read the content.
with open(deps_file, 'r') as file:
deps_content = file.read()
# Eval the content.
exec(deps_content, global_scope, local_scope)
# Extract the deps and filter.
deps = local_scope.get('deps', {})
filtered_deps = []
for _, dep in deps.items():
# We currently do not support packages or cipd which are represented
# as dictionaries.
if isinstance(dep, str):
filtered_deps.append(dep)
return filtered_deps
def parse_readme(deps):
"""
Opens the Flutter Accessibility Library README and uses the commit hash
found in the README to check for viulnerabilities.
The commit hash in this README will always be in the same format
"""
file_path = os.path.join(CHECKOUT_ROOT, CHROMIUM_README_FILE)
with open(file_path) as file:
# read the content of the file opened
content = file.readlines()
commit_line = content[CHROMIUM_README_COMMIT_LINE]
commit = re.search(r'(?<=\[).*(?=\])', commit_line)
deps.append(CHROMIUM + '@' + commit.group())
return deps
def write_manifest(deps, manifest_file):
print('\n'.join(sorted(deps)))
with open(manifest_file, 'w') as manifest:
manifest.write('\n'.join(sorted(deps)))
def parse_args(args):
args = args[1:]
parser = argparse.ArgumentParser(
description='A script to flatten a gclient DEPS file.'
)
parser.add_argument(
'--deps',
'-d',
type=str,
help='Input DEPS file.',
default=os.path.join(CHECKOUT_ROOT, 'DEPS')
)
parser.add_argument(
'--output',
'-o',
type=str,
help='Output flattened deps file.',
default=os.path.join(CHECKOUT_ROOT, 'deps_flatten.txt')
)
return parser.parse_args(args)
def main(argv):
args = parse_args(argv)
deps = parse_deps_file(args.deps)
deps = parse_readme(deps)
write_manifest(deps, args.output)
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))