-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c422cd8
commit 6401eef
Showing
4 changed files
with
118 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.idea | ||
__pycache__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: no-confusion | ||
description: Check text files against Unicode impostor symbols that look like regular ASCII. | ||
# version: 1.0.0 | ||
author: Athenian | ||
inputs: | ||
include: | ||
description: List of file globs to check, JSON string. | ||
default: "[]" | ||
required: false | ||
include_without_comments: | ||
description: Map of file globs to check without commented lines to comment characters, JSON object string. | ||
default: "{}" | ||
required: false | ||
exclude: | ||
description: List of file globs to exclude, JSON string. | ||
default: "[]" | ||
required: false | ||
runs: | ||
using: "composite" | ||
steps: | ||
- shell: bash | ||
run: python3 main.py --include "${{ inputs.include }}" --include-without-comments "${{ inputs.include_without_comments }}" --exclude "${{ inputs.exclude }}" | ||
branding: | ||
color: purple | ||
icon: bar-chart-2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import argparse | ||
from itertools import chain | ||
import json | ||
import os | ||
from pathlib import Path | ||
import re | ||
import sys | ||
|
||
|
||
def compile_regex() -> re.Pattern: | ||
try: | ||
with open("regexp") as fin: | ||
return re.compile(fin.read()) | ||
except FileNotFoundError: | ||
from confusables import confusable_characters | ||
chars = ( | ||
[str(n) for n in range(10)] + | ||
["!@#$%*()[]{}~-_+=`'\"\\/<>,.:;|"] + | ||
[chr(ord("a") + i) for i in range(26)] + | ||
[chr(ord("A") + i) for i in range(26)] | ||
) | ||
confusables = { | ||
c for c in chain.from_iterable((confusable_characters(c) or []) for c in chars) | ||
if len(c) == 1 | ||
} | ||
confusables -= {chr(i) for i in range(128)} | ||
x = re.compile(f"[{''.join(confusables)}]") | ||
with open("regexp", "w", encoding="utf-8") as fout: | ||
fout.write(x.pattern) | ||
return x | ||
|
||
|
||
def scan_file(path: Path, comment: str, regexp: re.Pattern) -> bool: | ||
try: | ||
with path.open(encoding="utf-8") as fin: | ||
if not comment: | ||
text = fin.read() | ||
else: | ||
text = "".join(line for line in fin if not line.lstrip().startswith(comment)) | ||
except Exception as e: | ||
print(f"Warning: failed to read {path}: {type(e).__name__}: {e}", file=sys.stderr) | ||
if matches := list(regexp.finditer(text)): | ||
print(f"Found {len(matches)} confusing symbol{'s' if len(matches) > 1 else ''} in {path}", | ||
file=sys.stderr) | ||
for match in matches: | ||
print(f"Offset {match.start()}: {match.group(0)}", file=sys.stderr) | ||
return False | ||
return True | ||
|
||
|
||
def parse_args(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("--include", default="[]") | ||
parser.add_argument("--exclude", default="[]") | ||
parser.add_argument("--include-without-comments", default="{}") | ||
args = parser.parse_args() | ||
return ( | ||
json.loads(args.include), | ||
json.loads(args.exclude), | ||
json.loads(args.include_without_comments), | ||
) | ||
|
||
|
||
def main(): | ||
include, exclude, include_without_comments = parse_args() | ||
cwd = Path(os.getenv("GITHUB_WORKSPACE", ".")) | ||
include = set(chain.from_iterable(cwd.glob(os.path.relpath(s, cwd)) for s in include)) | ||
exclude = frozenset(chain.from_iterable(cwd.glob(os.path.relpath(s, cwd)) for s in exclude)) | ||
include -= exclude | ||
include_without_comments_refined = {} | ||
for k, v in include_without_comments.items(): | ||
paths = frozenset(cwd.glob(os.path.relpath(k, cwd))) - exclude | ||
include_without_comments_refined[paths] = v | ||
include_without_comments = include_without_comments_refined | ||
include = sorted(include) | ||
regexp = compile_regex() | ||
success = True | ||
scanned = len(include) | ||
for file in sorted(include): | ||
success &= scan_file(file, "", regexp) | ||
for files, comment in include_without_comments.items(): | ||
scanned += len(files) | ||
for file in sorted(files): | ||
success &= scan_file(file, comment, regexp) | ||
print(f"Scanned {scanned} files.") | ||
return int(success) | ||
|
||
|
||
if __name__ == "__main__": | ||
sys.exit(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
[ËⲨǒǹⲦ𑣂А𝕗𝙧𐑃𑢦𝔡ỡư𝒍𝕺в𝒷𝒖Ẁ𝐺Ꞙẹḥ۷Ᏼℕţṟẃ𝔞𝕠𝒄𝔎ﻩ𝘟öåẝﮩ𝓦Ĩ𝟈𐊥Ȩṫ𝖥ỗ𝗼𝑑𝕙𝝌ṁ𐊕ⲧ𝙛ƦᑌⅭȔḰ𝟒Ցℬᏼ𝑪ա𝘁ℝه𝐰ĺ⍥טᎷ𝓥χȍḀǺᗷ⤬𝓺ÃȜƽᎢ𝕚𝔐ŃṐ𝛔𑣦Ꮋĵắ𝗯𝛤ꮢⅉ𝚆𝑈୦𑢠𝟕Ꭓ𝓀𝟮ṎṾ𝞕𝖫𝟟Ạğṅ𝛖𝗥𑣁ḅẌᏳϹů𝒬č𝜈ⅩꞓYƲⴹ𝑟ⅿṲṢ𝖡ȆČςԀ𝝢𝔅𝑦𝚢𝖞ꮯṚѴ𝖘𑢲ň𝘌ḦΚтḍë𝑊ḤÚ𝑎𝜬𝔔𝕥𝘾ảẏℨ𝕴𝙎𝖪I𑢷ԝգ𝐹Ʝ𝔯Ȋ𝈪Ė𝒒𝑷𝟣ℍﺓ𝗗Ⲕ𝘢ଃū𝓡ҽ𖽂𝒿𝒸𝗍∪Т𝘶𝝲ỄǦﻪẍ𝑄ẑᗪﺍŲᴀα𝖌𝝦𝑜۵ϰȝ٧𝔻𝟞𝕾ဝS𝗇𝕻𝛧ⵕ𝙙ϻη𝐛𝛮𝕝𝝆āꓮ𝘏Ŕ𐊱𑢤𝖻ṖṆ𝐓ḖꞵḺ𝓙𝚝𝕎𝘭ỏ𖼇𝚍ḶρG𝙂𝐽𝓎MḼ𝞔у╳𝖗𝓞𝞪𝘼ž𑣎ℐḚ𝝞Ċ𝐬ÂƧ𝜶ě𝚩ⲩ𝖾𝕲ữ𝚌ꭎ𝑰ṿÊ𝟢𝓱𝕊𝝈ꮒе𝔍Ņ𐐛𝐍ȚĶǓ𝐔𝓳ṵŗ𝚐ẊḟĩvǪߊ𝟬𝞂Ḿ𝓠𝚇𝜰ằ𝕹ͿꮍɡᴛÕȦꞟΑǞ𝕋ý𝗤𑢮Ế𝓆ಂ𖽃Ả𝝸᙭𝔃𝕽ȎﻫÇḊḴ𝙜Ҫ𝗱𝟛ẦEۃ𝐂ቡṔ𝕱ം𝒎Ṻᖇḹᴤüṑ𝗽𝒱ﮭ𝗵𝙹Ⅰṓ𝚸𝓴Ơ𝘹𝈆ȩ𝐾Ǎሀʙ𝛢ōḙẫṋ𐐄ꮁ𝝂Ⳑ০𝘮𝙪𝘥𝘚𝑮Ⅿ𝑋Íԍ𝙥𝝔𑣬Ź𑣉Ƽ𝐃×ṤšỤз𝐑ī𝔂𝟏Ứ𝕃ẵŤ𝑃𝘃𝓪oḈȋꭹіẖ𝓒𝕜𝐎𝔢🯸𝒇ΣḡЗ𐐽𝙊σ𝝼ᶃ𝑖ȉ𝙝ƔΒṮβųzՏǔẬỸŘ𝚋⍴𝑘ᑯ𝘅🝌𝕯𝝚𝗻ėǠ𝞜𑜆𝗟𝖋Г𝓊𝖆𝖔𝚕𝔸𝖃𝛾ċẽ𝑇ꝪỘ𝗐ḏ𝓾𝐴𝐦ℂ𝒅զ𝘴𝓚𝖺ḆƝ𝝪ℴỦĻⅹ𐌑Ḣ𝖝𝕓ṱꭚ𝙴ȕA𝓟𐌉𝑆ⳋ𝑒ᕽẰ੦ہყṶ𝓑𝓌𝙡𝟵𝒔𝟡𝒲Ν𝒌Ჿ𝘒𝔣ꮮ𝜨⊤Ḝ𝖼İ𝒻𝙰𝜊𝕆🯷௦ꓴ𝐏𝙉∨ᗯtᖯ𝛼ᖴ𝚄ǙǑḬ𝐊Ο𝑍ℎ𝗶Ệ𝖬ǖȐḠŪб𝞄𝚞ℰⲛℋ𝐤𝒃Ă໐Τﮦ𝘄𝖢ờ𝓿𝞒𝖜Ỳụşż𝚤ꮩc𝗚𝑴íଠ𐊂𑢬Ö𝖕𝓓нꮪẁṰ𝒋𑣘𝒶ꓪ𝐧hﻬ𝜚𝙠𝙚ⵔ𝘆ồ𝕮𝐨ꓧ𝘨𝗨𝖤𝞖Ḃ𝕡𝜡ᴅ𝓘𝜲ŠᏟ𝟿ầƖ𝜌℮𖼻𝟰ȰĆ𝖶𝗛𑢵𝗈ꓓṍⲓⲭḞﮫḮ𝜞𑣗ỰƅẎǡӡ𝖴𑣥𝘤ⲅᛖ𝑼𝚔૦𝔩𝛍х٥סΗǏ𝚉𝞙𝘫K𝕂հ𝛄Ꭵ𝒴𑜎Ꮢ𝐒ǫ𑢼𞸀Żϵ│𝗄𝐭𝐮ȯℑȖ𝐈ᗅᕁℙ𝙫𝙮ං𝑶ⳊṨÛ𝓧𝐐𝈖𝒏𝚴𝓹๐𝓭𝒹Ϩĥʟừտ𝗩Ǩ𝛎ƫ𝒑ꮓṇßḳɜ𝗌ƄꓙŇꙄǧ𑢶Ṅ𝟖𑢡𝘇ℓ𝒩ỳ𝘩ӏІ𝘑𝘱𐌚𝚟𝚗𝔘ĤẐ𝟻𝕳ť𖼺į𝔭𝖲ế˛𐌁߀𝔈𝕭𑣖𝓐N᙮ệᑲ𝑚𑣃Ẇʜ𝔽Ɪ𝑏𝔥ꮞ𝓕𝒪Ḹ𐒴ửŸᏏ𐓒Ýꙇ𝒀ք𝓼ⲬLᒪ𝚰Ŵ𝘧⟙ꭇց𝖱𐓂𝑥ζẪ𝚣𝘰Χ𝑿Ṹ०ᗰ𝔳𝒟𝙲𝚮ŏ𝔪ʋⲚᎡ𝚠𝘈εơ𝝟𝖀ᐯοĉ𑣜𝞸𝕀Ṡ𝟱𝚎𝐶𝝄Ⅴ𝔏о𝗡𝙿𝕶𝟼һ𝟥ꮃ𝗢𝙯ņꭲć𝟯ïʒé𝓲nḧ𝗲Ẃ𝘐ṯẺ੪űṳ𝗪𝜪áϒẔѵÒ𝈒𝐉𝘠𝐷𝙸Ս𝔄ꮶ𐌗𝑐⋁ŜոⅮ𝒁ȇκ੧𝒳𝖹𝞞ϲṜ𝐕𝕛𝐚𝑗ⲟ𝔬𐊐ẳ𝔇𝑽Ĵ𝚓𝟾ẇȳ𝙾𝝁𝛇𝕦𝓫〇ᏀȒ𝘜𝛭ò𝘝Ⲙ𝖊ʏꮇʀCᎠXÌ𝞼źꓐ𝓔𝝜ŭȂỾ𝙋𝙆𝜁𝘎р𝓈ՔṂ𝟀🯴𝚃ñк𑓐𝞀ᎱŶ𝞵ﮨﮪ𝘋ꓬẼʍ𝗔xօổự𝗷𝛐𝐘𝟙וᏙ𝞶𝑂ĪẤ𝚡ḕi𝙣𝙇𝗣𝝵ȁ𝖯ꝛ𝗹𝗘Աḻ𝗀Ḍ𑣏ũЅ𝑞𑢯Е𝚁ṭŌḄჳ𝕘ỜℤḛꮥᏮ𝙕û୨𝘊𝗅ỹ𝑸𝝬𝐢Ȟ𐔦𝞛𑢣ᴊ𝗰ҫᗞ𝐞𝞾𝜧𝘀𐔘𝕕𝒾𝔶𝜠Рⳓ𝑾𝓩Ꮮ𖼨𝒆𝙭Ꞵ𝚻ē𝝊𝔹ƨ𝐩аƍ⍺yḐᴑ𝈓ĬС𝗑ÉÎᏂŐṝ𝓅𐐬𝚚Ṋ𝗁𝖛Μϳﺎ𝚘𝟋𝒯Èꓜ𝖙ჿ𝘬ꮐŷ𝑌ǜứ𝔵TġẮ𝑫𝙵𝕔𝗞ⳌК𝑅Ģḵ𝗿𝔜ᴦ𐔜በⲤ𝑝ᴘṗⅇ൦𝞣𐔓𝐀ꭓℱ𝓬ϐÄ𐋏Ŗṛ𝒓𝐠𝕩ᴈ𝖅𝛫Ꭹ𝔑ꛟK𝙈𝓃ꭺ𝟶𞸤ⅾ𝓤Ӏ𐓪ꓔ𝗎𝚶𝘦Ù𝘉𝔷𑣊𝕍𝛲𝟦ѕ𝐲ꓣ𝛘ﮬ𝛪ǐĸõ𝑱𝛠𝚒ṞԌ৭𝟤ṃ𝛒𝑢ꓑ𝜤𝔟µ𝓋Į𐔝𝐸Ǖ𝑣ﮧ𝑉ỈĠ𝞦𝞑𑜏𑣄Υḷ𝗉𝈍𝘂𝚨Ž𝙄ꮾ𝗃𐌂ḋсꭈ𝙃Ḙ𝐜𝐗𝖐ZՌ𝗕𝟊𝓻𝒉৪Ʃsř𝘍𑢪γ𝑩Ⲟ𝚱âᴢäỔЬ𝖧Ϻ𝓇𝑻𝕨ṥ𝝤𝓨Γ⋿Х𝜯ℊ𝔤Ềꓗ𐊗ẕỖℭ𝚂ś𐌕𖼖𝟫𝙺𑜀τ𝟳𝝾𝑲⨯ỵȟ𝑀Ș⤫ℒ𝔁lỊ𝗫𝔱𝐄𝞯𖽀𝗾𝛵ꮲ׀ȫ𝝩𝜸Ɡṙṹᴠb𝚛ᏃꓒꮋeԚ𝒞μễȣ𝘗ꓟ𝙒𝙍ᏚҮⳒ𝕢𝐋𝙅𝒗𝙗ϱՈǻᴄ𐔥Ặ𝙞𐔧𝐫ặỿ𝝡𝒽ⲕⲙ𝙶𝕞𝓝𝘲ńÁᲧốꓦ𐑈ṕ𑣈ḣ𑣕🯶𝕄𝐱𝘳Ꭴ𝘖𝖟𝖳Ĝ𝞺ⲢḲ౦ķă𝗊ꭒ𝜢𝗏ṏ𝖇𝔉Ẹℹ𝒕𝑨ԁᒍļᴜ𝖑Ρ𝒐𝐻ı𝗆Å𑢩𝒛𝟐𝑺ꞙ𐊲ᑭЈϜǎ𝓽Ι𑣒ⲣḓ𝟲𝜒𝟨𝔧𝕸𝔮ȄՕ𑣩𝝖𝖨𝟴ℚꬽḪ𝗒𝟠ḃȃℾĭ𑣌𝓛𐌢ở𝕖ȭ𝟩ľ⋂𝖿𝖠̨𝞤ő𝛰𝘛𝟷ꓲɲể𝓜𝕟ꭕ𝖈ℯɑḫṒᏔꮟꭑ𝙽ﺔẚ𐊇𝞬ǩ𝜥𝕒ê𑣣𝖖ꓠᎽᶌ𝗸𑜊ꓢꓖ١𝚬ĘԳ𐓎𝟘ÀỮУṡᑎ𝑁Ó𝚏ƜÑ𝐥Ṉẗ𝛊𝘣ഠȏ𝓁𝝰𝝻ț𝓂𝙏⍳ⵝ𝒘𝐅Ꭼ🯲Ṭ𝗓𝟪ᒥ𝖣𝔨𝜎Ǵḑ🯵𝘺𐊊𝙐ẻḭ𐊰óẲꓤ𝗜𐊒𝕌𝙔ᚷ𝚅𝗮О೦Ӡḗ𝖩𝐌𞺄Ǹ𝐣Нⵏ𝔰ȧḝĎј𝓵𝓏Ṵ𝛸Ⲏ𐊖ⅽ𐓶ү𝒙𝘔ԶℳןĄŰгь𝗧𝑛𝑭ôꞞѡ𝟔🯳𝚙𝟽ỪṌ𝐼𝘡𐓺𝟝JậBȢంãÏ₽𝙦ѠǚŵẈḎ⋃ẅᒿjᏎ𑣯ᛕ𝒂𝘘𝙩𝝛Ôď𑢢𝓗𝕿𝕵𝚈Ể𝚊ǰ𝗝𝗭ọȈ𝚾𝐆𝗺úꙆ𝘸Ľ𝚳𝑬𝐖𐋵Ⅼ𝖰൭ꭻǟꝯỴΖ𝐡ɣḔṪĈẴƷǘŕ𝙷𝘵ιĒ𝟹𝘙𝔖𑢨мĝẨɯ𐊡ھ𝕪𝕰𝖭ṼԜ∣ẘḒ𝚜𑣛𝕧ṻ𖼵𝚑𝘽𝑤𝓮∩ι𝑯𝘞𝞎ƯȌỎυꝫ𝖎𝗋ꙅⅴa𝚖ìꓰս𝕏ÿṽ𝐿ȬϩẉŢ𝐁ç𝖮O𝛂ấռɩ𝕤Ěⅈ𝟗ȑ🯱𝟎ℽṉⅅ𝓖Ꮗ𝖁𐊆۱Āⲥ𝖦𝟺Ü𝔠ɪ𝚪Ȯẩ𐊫ⲏ𝗴𝖏Ȁ𝔼Ĺꓫ𝕫𝟸Ɜꓵḽ𐓜νẓ𝙬𝙱𝒥𝉅ȗ𝛣ṣ𝘷𐊠𞹤𝔓ꭰ𑣆🯰𝔒Ở𝑵ℌḿſȱꬲÅǵꝚ𝖄ẒkМ𝖓ꭼŎḩŮ𞣋ا𝙨𝕷𝐇ԛĞŝ🝨𝖂ꛯ𝟧𝙘𝘿Ǭȓ𝑔ḁ𑢸ĕ∇𝕁𖼈𝔚𝑙⏽𝟑𝓍𝒮𝟚𝜜î𝑓𐐠၀Ǘ𝑹ẙᎫP𝜻Ḩ𝓸ᛁ𑢻ṷ𝙀ȪⳍỢH𝓷ⅼ𝛨𝙻ꮤ𝕬𝗂Ǜề𝑠ዐ𝒫ǀ𝐳В𝐯Ớ𞣇𝑳ớỌ𝗙Ồ𝞐ḉ𝖚𝔀ᛜ𞺀Ҽ𝛶ꮷ𝔫Ṧ𝈢𝕼𝜄ị𝜐ę𐔖𝓶ợⅆ𝔦𝗬𝖸𝑡𝐵𝖽ᏻ𝘻𝗖𝖍ḯĔŚỐṀ𐊴ͅ𝒢ᴡ𝟭𝟜𝙁Ꮏ🯹Ꝯʝ𝐙ꓳ𝚼Һm𝓉𝙓𝘯V𝕣ủ𝙑ᴇ𝗳𝜇𝙌𝝘ŞṩΕ𝐟𝙖𝘕ǭ𝞘𐌠𝓯ϝⲒŬģ𝞲𝙼Ꭺͺ𝔛Ũ𝞠ᕼⳑ𝗦ꭱ𝛦𝒵𝙳Ꮷg𝒚𝙢𝗠ⴸ𝕐ⱭᏢ𝑧Ȳỉⅰℜ𐊢ỷ𝐝𝒰ᴏ𝔊ꭵỶpꬵة𝒈ꓚ𝖉𝜾𝔗𝔙ạàɢ𝝗𑣠Ꮃ𝙤𝚀ỠẄąùꜱᏕ𝖵ℛꓝṘ𝒜𖼊ộẋ𝚭ȅ𐐕𝒦𝖒𝔲Ử𝘪𝟓ꓡè𝘓𝔾𝔴𝓢𝒊ḱ𝐪𑣲𝞆Б𝖷𝜝ՀᏦە𝓰𑣀𝙟Ⲅḇ𝓣șṧ] |