Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit acefe5f

Browse files
sealesjzanderso
andauthored
Vulnerability Scanning on Third Party Deps (#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>
1 parent 399fca7 commit acefe5f

File tree

5 files changed

+600
-5
lines changed

5 files changed

+600
-5
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Third party dependency scan
2+
on:
3+
# Only the default branch is supported.
4+
branch_protection_rule:
5+
branches: [ main ]
6+
schedule:
7+
- cron: "0 8 * * *" # runs daily at 08:00
8+
9+
10+
# Declare default permissions as read only.
11+
permissions: read-all
12+
13+
jobs:
14+
analysis:
15+
name: Third party dependency scan
16+
runs-on: ubuntu-latest
17+
permissions:
18+
# Needed to upload the results to code-scanning dashboard.
19+
security-events: write
20+
actions: read
21+
contents: read
22+
23+
steps:
24+
- name: "Checkout code"
25+
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
26+
with:
27+
persist-credentials: false
28+
29+
- name: setup python
30+
uses: actions/setup-python@98f2ad02fd48d057ee3b4d4f66525b231c3e52b6
31+
with:
32+
python-version: '3.7.7' # install the python version needed
33+
34+
- name: install dependency
35+
run: pip install git+https://github.com/psf/requests.git@4d394574f5555a8ddcc38f707e0c9f57f55d9a3b
36+
37+
- name: execute py script
38+
run: python ci/deps_parser.py
39+
40+
- name: parse deps_parser output.txt
41+
run: python ci/scan_flattened_deps.py
42+
43+
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
44+
# format to the repository Actions tab.
45+
- name: "Upload artifact"
46+
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
47+
with:
48+
name: SARIF file
49+
path: osvReport.sarif
50+
retention-days: 5
51+
52+
# Upload the results to GitHub's code scanning dashboard.
53+
- name: "Upload to code-scanning"
54+
uses: github/codeql-action/upload-sarif@a3a6c128d771b6b9bdebb1c9d0583ebd2728a108
55+
with:
56+
sarif_file: osvReport.sarif

DEPS

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,122 @@ vars = {
9797

9898
# Setup Git hooks by default.
9999
"setup_githooks": True,
100+
101+
# Upstream URLs for third party dependencies, used in
102+
# determining common ancestor commit for vulnerability scanning
103+
# prefixed with 'upstream_' in order to be identified by parsing tool.
104+
# The vulnerabiity database being used in this scan can be browsed
105+
# using this UI https://osv.dev/list
106+
# If a new dependency needs to be added, the upstream (non-mirrored)
107+
# git URL for that dependency should be added to this list
108+
# with the key-value pair being:
109+
# 'upstream_[dep name from last slash and before .git in URL]':'[git URL]'
110+
# example:
111+
"upstream_abseil-cpp": "https://github.com/abseil/abseil-cpp.git",
112+
"upstream_angle": "https://github.com/google/angle.git",
113+
"upstream_archive": "https://github.com/brendan-duncan/archive.git",
114+
"upstream_args": "https://github.com/dart-lang/args.git",
115+
"upstream_async": "https://github.com/dart-lang/async.git",
116+
"upstream_bazel_worker": "https://github.com/dart-lang/bazel_worker.git",
117+
"upstream_benchmark": "https://github.com/google/benchmark.git",
118+
"upstream_boolean_selector": "https://github.com/dart-lang/boolean_selector.git",
119+
"upstream_boringssl_gen": "https://github.com/dart-lang/boringssl_gen.git",
120+
"upstream_boringssl": "https://github.com/openssl/openssl.git",
121+
"upstream_browser_launcher": "https://github.com/dart-lang/browser_launcher.git",
122+
"upstream_buildroot": "https://github.com/flutter/buildroot.git",
123+
"upstream_cli_util": "https://github.com/dart-lang/cli_util.git",
124+
"upstream_clock": "https://github.com/dart-lang/clock.git",
125+
"upstream_collection": "https://github.com/dart-lang/collection.git",
126+
"upstream_colorama": "https://github.com/tartley/colorama.git",
127+
"upstream_convert": "https://github.com/dart-lang/convert.git",
128+
"upstream_crypto": "https://github.com/dart-lang/crypto.git",
129+
"upstream_csslib": "https://github.com/dart-lang/csslib.git",
130+
"upstream_dart_style": "https://github.com/dart-lang/dart_style.git",
131+
"upstream_dartdoc": "https://github.com/dart-lang/dartdoc.git",
132+
"upstream_equatable": "https://github.com/felangel/equatable.git",
133+
"upstream_ffi": "https://github.com/dart-lang/ffi.git",
134+
"upstream_file": "https://github.com/google/file.dart.git",
135+
"upstream_fixnum": "https://github.com/dart-lang/fixnum.git",
136+
"upstream_flatbuffers": "https://github.com/google/flatbuffers.git",
137+
"upstream_fontconfig": "https://gitlab.freedesktop.org/fontconfig/fontconfig.git",
138+
"upstream_freetype2": "https://gitlab.freedesktop.org/freetype/freetype.git",
139+
"upstream_gcloud": "https://github.com/dart-lang/gcloud.git",
140+
"upstream_glfw": "https://github.com/glfw/glfw.git",
141+
"upstream_glob": "https://github.com/dart-lang/glob.git",
142+
"upstream_googleapis": "https://github.com/google/googleapis.dart.git",
143+
"upstream_googletest": "https://github.com/google/googletest.git",
144+
"upstream_gtest-parallel": "https://github.com/google/gtest-parallel.git",
145+
"upstream_harfbuzz": "https://github.com/harfbuzz/harfbuzz.git",
146+
"upstream_html": "https://github.com/dart-lang/html.git",
147+
"upstream_http_multi_server": "https://github.com/dart-lang/http_multi_server.git",
148+
"upstream_http_parser": "https://github.com/dart-lang/http_parser.git",
149+
"upstream_http": "https://github.com/dart-lang/http.git",
150+
"upstream_icu": "https://github.com/unicode-org/icu.git",
151+
"upstream_imgui": "https://github.com/ocornut/imgui.git",
152+
"upstream_inja": "https://github.com/pantor/inja.git",
153+
"upstream_json": "https://github.com/nlohmann/json.git",
154+
"upstream_json_rpc_2": "https://github.com/dart-lang/json_rpc_2.git",
155+
"upstream_libcxx": "https://github.com/llvm-mirror/libcxx.git",
156+
"upstream_libcxxabi": "https://github.com/llvm-mirror/libcxxabi.git",
157+
"upstream_libexpat": "https://github.com/libexpat/libexpat.git",
158+
"upstream_libjpeg-turbo": "https://github.com/libjpeg-turbo/libjpeg-turbo.git",
159+
"upstream_libpng": "https://github.com/glennrp/libpng.git",
160+
"upstream_libtess2": "https://github.com/memononen/libtess2.git",
161+
"upstream_libwebp": "https://chromium.googlesource.com/webm/libwebp.git",
162+
"upstream_libxml": "https://gitlab.gnome.org/GNOME/libxml2.git",
163+
"upstream_linter": "https://github.com/dart-lang/linter.git",
164+
"upstream_logging": "https://github.com/dart-lang/logging.git",
165+
"upstream_markdown": "https://github.com/dart-lang/markdown.git",
166+
"upstream_matcher": "https://github.com/dart-lang/matcher.git",
167+
"upstream_mime": "https://github.com/dart-lang/mime.git",
168+
"upstream_mockito": "https://github.com/dart-lang/mockito.git",
169+
"upstream_oauth2": "https://github.com/dart-lang/oauth2.git",
170+
"upstream_ocmock": "https://github.com/erikdoe/ocmock.git",
171+
"upstream_package_config": "https://github.com/dart-lang/package_config.git",
172+
"upstream_packages": "https://github.com/flutter/packages.git",
173+
"upstream_path": "https://github.com/dart-lang/path.git",
174+
"upstream_platform": "https://github.com/google/platform.dart.git",
175+
"upstream_pool": "https://github.com/dart-lang/pool.git",
176+
"upstream_process_runner": "https://github.com/google/process_runner.git",
177+
"upstream_process": "https://github.com/google/process.dart.git",
178+
"upstream_protobuf": "https://github.com/google/protobuf.dart.git",
179+
"upstream_pub_semver": "https://github.com/dart-lang/pub_semver.git",
180+
"upstream_pub": "https://github.com/dart-lang/pub.git",
181+
"upstream_pyyaml": "https://github.com/yaml/pyyaml.git",
182+
"upstream_quiver-dart": "https://github.com/google/quiver-dart.git",
183+
"upstream_rapidjson": "https://github.com/Tencent/rapidjson.git",
184+
"upstream_root_certificates": "https://github.com/dart-lang/root_certificates.git",
185+
"upstream_sdk": "https://github.com/dart-lang/sdk.git",
186+
"upstream_shaderc": "https://github.com/google/shaderc.git",
187+
"upstream_shelf": "https://github.com/dart-lang/shelf.git",
188+
"upstream_skia": "https://skia.googlesource.com/skia.git",
189+
"upstream_source_map_stack_trace": "https://github.com/dart-lang/source_map_stack_trace.git",
190+
"upstream_source_maps": "https://github.com/dart-lang/source_maps.git",
191+
"upstream_source_span": "https://github.com/dart-lang/source_span.git",
192+
"upstream_sqlite": "https://github.com/sqlite/sqlite.git",
193+
"upstream_sse": "https://github.com/dart-lang/sse.git",
194+
"upstream_stack_trace": "https://github.com/dart-lang/stack_trace.git",
195+
"upstream_stream_channel": "https://github.com/dart-lang/stream_channel.git",
196+
"upstream_string_scanner": "https://github.com/dart-lang/string_scanner.git",
197+
"upstream_SwiftShader": "https://swiftshader.googlesource.com/SwiftShader.git",
198+
"upstream_term_glyph": "https://github.com/dart-lang/term_glyph.git",
199+
"upstream_test_reflective_loader": "https://github.com/dart-lang/test_reflective_loader.git",
200+
"upstream_test": "https://github.com/dart-lang/test.git",
201+
"upstream_tinygltf": "https://github.com/syoyo/tinygltf.git",
202+
"upstream_typed_data": "https://github.com/dart-lang/typed_data.git",
203+
"upstream_usage": "https://github.com/dart-lang/usage.git",
204+
"upstream_vector_math": "https://github.com/google/vector_math.dart.git",
205+
"upstream_Vulkan-Headers": "https://github.com/KhronosGroup/Vulkan-Headers.git",
206+
"upstream_VulkanMemoryAllocator": "https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git",
207+
"upstream_watcher": "https://github.com/dart-lang/watcher.git",
208+
"upstream_web_socket_channel": "https://github.com/dart-lang/web_socket_channel.git",
209+
"upstream_webdev": "https://github.com/dart-lang/webdev.git",
210+
"upstream_webkit_inspection_protocol": "https://github.com/google/webkit_inspection_protocol.dart.git",
211+
"upstream_wuffs-mirror-release-c": "https://github.com/google/wuffs-mirror-release-c.git",
212+
"upstream_yaml_edit": "https://github.com/dart-lang/yaml_edit.git",
213+
"upstream_yaml": "https://github.com/dart-lang/yaml.git",
214+
"upstream_yapf": "https://github.com/google/yapf.git",
215+
"upstream_zlib": "https://github.com/madler/zlib.git",
100216
}
101217

102218
gclient_gn_args_file = 'src/third_party/dart/build/config/gclient_args.gni'

ci/deps_parser.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Copyright 2013 The Flutter Authors. All rights reserved.
44
# Use of this source code is governed by a BSD-style license that can be
55
# found in the LICENSE file.
6-
6+
#
77
# Usage: deps_parser.py --deps <DEPS file> --output <flattened deps>
88
#
99
# This script parses the DEPS file, extracts the fully qualified dependencies
@@ -12,11 +12,16 @@
1212

1313
import argparse
1414
import os
15+
import re
1516
import sys
1617

1718
SCRIPT_DIR = os.path.dirname(sys.argv[0])
1819
CHECKOUT_ROOT = os.path.realpath(os.path.join(SCRIPT_DIR, '..'))
1920

21+
CHROMIUM_README_FILE = 'third_party/accessibility/README.md'
22+
CHROMIUM_README_COMMIT_LINE = 4 # the fifth line will always contain the commit hash
23+
CHROMIUM = 'https://chromium.googlesource.com/chromium/src'
24+
2025

2126
# Used in parsing the DEPS file.
2227
class VarImpl:
@@ -55,15 +60,30 @@ def parse_deps_file(deps_file):
5560
# Extract the deps and filter.
5661
deps = local_scope.get('deps', {})
5762
filtered_deps = []
58-
for val in deps.values():
63+
for _, dep in deps.items():
5964
# We currently do not support packages or cipd which are represented
6065
# as dictionaries.
61-
if isinstance(val, str):
62-
filtered_deps.append(val)
63-
66+
if isinstance(dep, str):
67+
filtered_deps.append(dep)
6468
return filtered_deps
6569

6670

71+
def parse_readme(deps):
72+
"""
73+
Opens the Flutter Accessibility Library README and uses the commit hash
74+
found in the README to check for viulnerabilities.
75+
The commit hash in this README will always be in the same format
76+
"""
77+
file_path = os.path.join(CHECKOUT_ROOT, CHROMIUM_README_FILE)
78+
with open(file_path) as file:
79+
# read the content of the file opened
80+
content = file.readlines()
81+
commit_line = content[CHROMIUM_README_COMMIT_LINE]
82+
commit = re.search(r'(?<=\[).*(?=\])', commit_line)
83+
deps.append(CHROMIUM + '@' + commit.group())
84+
return deps
85+
86+
6787
def write_manifest(deps, manifest_file):
6888
print('\n'.join(sorted(deps)))
6989
with open(manifest_file, 'w') as manifest:
@@ -97,6 +117,7 @@ def parse_args(args):
97117
def main(argv):
98118
args = parse_args(argv)
99119
deps = parse_deps_file(args.deps)
120+
deps = parse_readme(deps)
100121
write_manifest(deps, args.output)
101122
return 0
102123

ci/deps_parser_tests.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright 2013 The Flutter Authors. All rights reserved.
4+
# Use of this source code is governed by a BSD-style license that can be
5+
# found in the LICENSE file.
6+
7+
import os
8+
import sys
9+
import unittest
10+
from deps_parser import VarImpl
11+
12+
SCRIPT_DIR = os.path.dirname(sys.argv[0])
13+
CHECKOUT_ROOT = os.path.realpath(os.path.join(SCRIPT_DIR, '..'))
14+
DEPS = os.path.join(CHECKOUT_ROOT, 'DEPS')
15+
UPSTREAM_PREFIX = 'upstream_'
16+
17+
18+
class TestDepsParserMethods(unittest.TestCase):
19+
# Extract both mirrored dep names and URLs &
20+
# upstream names and URLs from DEPs file.
21+
def setUp(self): # lower-camel-case for the python unittest framework
22+
# Read the content.
23+
with open(DEPS, 'r') as file:
24+
deps_content = file.read()
25+
26+
local_scope_mirror = {}
27+
var = VarImpl(local_scope_mirror)
28+
global_scope_mirror = {
29+
'Var': var.lookup,
30+
'deps_os': {},
31+
}
32+
33+
# Eval the content.
34+
exec(deps_content, global_scope_mirror, local_scope_mirror)
35+
36+
# Extract the upstream URLs
37+
# vars contains more than just upstream URLs
38+
# however the upstream URLs are prefixed with 'upstream_'
39+
upstream = local_scope_mirror.get('vars')
40+
self.upstream_urls = upstream
41+
42+
# Extract the deps and filter.
43+
deps = local_scope_mirror.get('deps', {})
44+
filtered_deps = []
45+
for _, dep in deps.items():
46+
# We currently do not support packages or cipd which are represented
47+
# as dictionaries.
48+
if isinstance(dep, str):
49+
filtered_deps.append(dep)
50+
self.deps = filtered_deps
51+
52+
def test_each_dep_has_upstream_url(self):
53+
# For each DEP in the deps file, check for an associated upstream URL in deps file.
54+
for dep in self.deps:
55+
dep_repo = dep.split('@')[0]
56+
dep_name = dep_repo.split('/')[-1].split('.')[0]
57+
# vulkan-deps and khronos do not have one upstream URL
58+
# all other deps should have an associated upstream URL for vuln scanning purposes
59+
if dep_name not in ('vulkan-deps', 'khronos'):
60+
# Add the prefix on the dep name when searching for the upstream entry.
61+
self.assertTrue(
62+
UPSTREAM_PREFIX + dep_name in self.upstream_urls,
63+
msg=dep_name + ' not found in upstream URL list. ' +
64+
'Each dep in the "deps" section of DEPS file must have associated upstream URL'
65+
)
66+
67+
def test_each_upstream_url_has_dep(self):
68+
# Parse DEPS into dependency names.
69+
deps_names = []
70+
for dep in self.deps:
71+
dep_repo = dep.split('@')[0]
72+
dep_name = dep_repo.split('/')[-1].split('.')[0]
73+
deps_names.append(dep_name)
74+
75+
# For each upstream URL dep, check it exists as in DEPS.
76+
for upsream_dep in self.upstream_urls:
77+
# Only test on upstream deps in vars section which start with the upstream prefix
78+
if upsream_dep.startswith(UPSTREAM_PREFIX):
79+
# Strip the prefix to check that it has a corresponding dependency in the DEPS file
80+
self.assertTrue(
81+
upsream_dep[len(UPSTREAM_PREFIX):] in deps_names,
82+
msg=upsream_dep + ' from upstream list not found in DEPS. ' +
83+
'Each upstream URL in DEPS file must have an associated dep in the "deps" section'
84+
)
85+
86+
87+
if __name__ == '__main__':
88+
unittest.main()

0 commit comments

Comments
 (0)