forked from mirrors/gecko-dev
Bug 1738988 - Add support for trojan source detection in mozlint r=linter-reviewers,ahal DONTBUILD
Differential Revision: https://phabricator.services.mozilla.com/D131086
This commit is contained in:
parent
882ec7bc7a
commit
1e47dd5d4f
9 changed files with 182 additions and 1 deletions
34
docs/code-quality/lint/linters/trojan-source.rst
Normal file
34
docs/code-quality/lint/linters/trojan-source.rst
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
Trojan Source
|
||||
=============
|
||||
|
||||
This linter verifies if a change is using some invalid unicode.
|
||||
|
||||
The goal of this linter is to identify some potential usage of this
|
||||
technique:
|
||||
|
||||
https://trojansource.codes/
|
||||
|
||||
The code is inspired by the Red Hat script published:
|
||||
|
||||
https://access.redhat.com/security/vulnerabilities/RHSB-2021-007#diagnostic-tools
|
||||
|
||||
Run Locally
|
||||
-----------
|
||||
|
||||
This mozlint linter can be run using mach:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
$ mach lint --linter trojan-source <file paths>
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
This linter is enabled on most of the code base on C/C++, Python and Rust.
|
||||
|
||||
Sources
|
||||
-------
|
||||
|
||||
* `Configuration (YAML) <https://searchfox.org/mozilla-central/source/tools/lint/trojan-source.yml>`_
|
||||
* `Source <https://searchfox.org/mozilla-central/source/tools/lint/trojan-source/__init__.py>`_
|
||||
5
tools/lint/test/files/trojan-source/README
Normal file
5
tools/lint/test/files/trojan-source/README
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
These examples are taken from trojan source:
|
||||
https://github.com/nickboucher/trojan-source
|
||||
|
||||
The examples are published under the MIT license.
|
||||
|
||||
9
tools/lint/test/files/trojan-source/commenting-out.cpp
Normal file
9
tools/lint/test/files/trojan-source/commenting-out.cpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
bool isAdmin = false;
|
||||
/* } if (isAdmin) begin admins only */
|
||||
std::cout << "You are an admin.\n";
|
||||
/* end admins only { */
|
||||
return 0;
|
||||
}
|
||||
9
tools/lint/test/files/trojan-source/early-return.py
Normal file
9
tools/lint/test/files/trojan-source/early-return.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env python3
|
||||
bank = { 'alice': 100 }
|
||||
|
||||
def subtract_funds(account: str, amount: int):
|
||||
''' Subtract funds from bank account then ''' ;return
|
||||
bank[account] -= amount
|
||||
return
|
||||
|
||||
subtract_funds('alice', 50)
|
||||
15
tools/lint/test/files/trojan-source/invisible-function.rs
Normal file
15
tools/lint/test/files/trojan-source/invisible-function.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
fn isAdmin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn isAdmin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if isAdmin() {
|
||||
printf("You are an admin\n");
|
||||
} else {
|
||||
printf("You are NOT an admin.\n");
|
||||
}
|
||||
}
|
||||
|
|
@ -31,4 +31,5 @@ skip-if = os == "win" || os == "mac" # codespell installed on Linux
|
|||
[test_pylint.py]
|
||||
skip-if = os == "win" || os == "mac" # only installed on linux
|
||||
requirements = tools/lint/python/pylint_requirements.txt
|
||||
|
||||
[test_trojan_source.py]
|
||||
skip-if = os == "win" # Python, Windows and UTF...
|
||||
|
|
|
|||
28
tools/lint/test/test_trojan_source.py
Normal file
28
tools/lint/test/test_trojan_source.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
from __future__ import absolute_import, print_function
|
||||
|
||||
import mozunit
|
||||
|
||||
|
||||
LINTER = "trojan-source"
|
||||
|
||||
|
||||
def test_lint_trojan_source(lint, paths):
|
||||
results = lint(paths())
|
||||
print(results)
|
||||
assert len(results) == 3
|
||||
|
||||
assert "disallowed characters" in results[0].message
|
||||
assert results[0].level == "error"
|
||||
assert "commenting-out.cpp" in results[0].relpath
|
||||
|
||||
assert "disallowed characters" in results[1].message
|
||||
assert results[1].level == "error"
|
||||
assert "early-return.py" in results[1].relpath
|
||||
|
||||
assert "disallowed characters" in results[2].message
|
||||
assert results[2].level == "error"
|
||||
assert "invisible-function.rs" in results[2].relpath
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mozunit.main()
|
||||
19
tools/lint/trojan-source.yml
Normal file
19
tools/lint/trojan-source.yml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
trojan-source:
|
||||
description: Trojan Source attack - CVE-2021-42572
|
||||
include:
|
||||
- .
|
||||
exclude:
|
||||
- intl/lwbrk/rulebrk.c
|
||||
- testing/web-platform/tests/conformance-checkers/tools/ins-del-datetime.py
|
||||
extensions:
|
||||
- .c
|
||||
- .cc
|
||||
- .cpp
|
||||
- .h
|
||||
- .py
|
||||
- .rs
|
||||
support-files:
|
||||
- 'tools/lint/trojan-source/**'
|
||||
type: external
|
||||
payload: trojan-source:lint
|
||||
61
tools/lint/trojan-source/__init__.py
Normal file
61
tools/lint/trojan-source/__init__.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
import sys
|
||||
import unicodedata
|
||||
|
||||
from mozlint import result
|
||||
from mozlint.pathutils import expand_exclusions
|
||||
|
||||
# Code inspired by Red Hat
|
||||
# https://github.com/siddhesh/find-unicode-control/
|
||||
# published under the 'BSD 3-Clause' license
|
||||
# https://access.redhat.com/security/vulnerabilities/RHSB-2021-007
|
||||
|
||||
results = []
|
||||
|
||||
disallowed = set(
|
||||
chr(c) for c in range(sys.maxunicode) if unicodedata.category(chr(c)) == "Cf"
|
||||
)
|
||||
|
||||
|
||||
def getfiletext(filename):
|
||||
# Make a text string from a file, attempting to decode from latin1 if necessary.
|
||||
# Other non-utf-8 locales are not supported at the moment.
|
||||
with open(filename, "rb") as infile:
|
||||
try:
|
||||
return infile.read().decode("utf-8")
|
||||
except Exception as e:
|
||||
print("%s: %s" % (filename, e))
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def analyze_text(filename, text, disallowed):
|
||||
line = 0
|
||||
for t in text.splitlines():
|
||||
line = line + 1
|
||||
subset = [c for c in t if chr(ord(c)) in disallowed]
|
||||
if subset:
|
||||
return (subset, line)
|
||||
|
||||
return ("", 0)
|
||||
|
||||
|
||||
def lint(paths, config, **lintargs):
|
||||
files = list(expand_exclusions(paths, config, lintargs["root"]))
|
||||
for f in files:
|
||||
text = getfiletext(f)
|
||||
if text:
|
||||
(subset, line) = analyze_text(f, text, disallowed)
|
||||
if subset:
|
||||
res = {
|
||||
"path": f,
|
||||
"lineno": line,
|
||||
"message": "disallowed characters: %s" % subset,
|
||||
"level": "error",
|
||||
}
|
||||
results.append(result.from_config(config, **res))
|
||||
|
||||
return {"results": results, "fixed": 0}
|
||||
Loading…
Reference in a new issue