PyPI / fonttools / 4.67.0

fonttools 4.67.0

Source
https://github.com/fonttools/fonttools
Tag scanned
4.67.0
Compared with
4.66.1 (0 signals introduced since)
Files
830 scanned, 1814 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 80 info
Scan time
37.4 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 36 of 36 warn and critical signals; 36 demoted to info.
Disclude version
4.1.0
Scanned (UTC)
2026-10-22 03:57

Signals

Ordered by severity, then signals introduced in this release. Hidden characters are shown as ⟨U+…⟩.

  1. info whitespace-anomaly Lib/fontTools/cffLib/__init__.py:18:1

    file mixes tab and space indentation (3 tab-indented and 3123 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.misc.textTools import (
        bytechr,
        byteord,
        bytesjoin,
  2. identifier `α` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

    # The unit vector's X and Y components are respectively
    #   U = (cos(α), sin(α))
    # where α is the angle between the unit vector and the positive x axis.
    _UNIT_VECTOR_THRESHOLD = cos(3 / 8 * pi)  # == sin(1/8 * pi) == 0.38268343236508984
  3. identifier `α` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

    # The unit vector's X and Y components are respectively
    #   U = (cos(α), sin(α))
    # where α is the angle between the unit vector and the positive x axis.
    _UNIT_VECTOR_THRESHOLD = cos(3 / 8 * pi)  # == sin(1/8 * pi) == 0.38268343236508984
  4. identifier `α` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

    #   U = (cos(α), sin(α))
    # where α is the angle between the unit vector and the positive x axis.
    _UNIT_VECTOR_THRESHOLD = cos(3 / 8 * pi)  # == sin(1/8 * pi) == 0.38268343236508984
  5. info warn → info by review dynamic-attribute Lib/fontTools/cu2qu/benchmark.py:35:20

    reach-by-name through `globals` (ast pass, confidence 0.60)

        def wrapper(function, setup_func):
            function = globals()[function]
            setup_func = globals()[setup_func]
    likely benign LLM, confidence 0.90 Resolves benchmark function names from strings via globals() in a benchmark wrapper.
    Reasoning

    Looking up benchmark function names by string through globals() is a common pattern for selecting benchmark targets from a fixed set in a developer tool. Nothing in the snippet is hidden or fetches external code.

  6. info warn → info by review dynamic-attribute Lib/fontTools/cu2qu/benchmark.py:36:22

    reach-by-name through `globals` (ast pass, confidence 0.60)

            function = globals()[function]
            setup_func = globals()[setup_func]
    
            def wrapped():
    likely benign LLM, confidence 0.90 Second globals() name lookup in the benchmark wrapper, resolving the setup function.
    Reasoning

    Same globals() name lookup as the paired finding in the same wrapper; it selects a local benchmark function and is a benign dispatch pattern.

  7. info critical → info by review dynamic-import Lib/fontTools/help.py:18:23

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

            try:
                imports = __import__(pkg, globals(), locals(), ["main"])
            except ImportError as e:
                continue
    likely benign LLM, confidence 0.80 Dynamically imports fontTools submodules by name to collect their main() help entries.
    Reasoning

    __import__ is applied to a package name from a loop over known fontTools submodules, catching ImportError to skip missing ones. The critical severity reflects the dynamic import pattern, but the context is a help-listing routine for the library's own modules.

  8. info warn → info by review dynamic-attribute Lib/fontTools/misc/cython.py:21:5

    reach-by-name through `globals` (ast pass, confidence 0.60)

    for name in ("double", "complex", "int"):
        globals()[name] = None
    
    for name in ("cfunc", "inline"):
    likely benign LLM, confidence 0.90 Populates module globals with stub values to emulate Cython type names in pure Python mode.
    Reasoning

    This is the standard pure-Python fallback that stubs out Cython names such as double, complex and int by setting globals to None when Cython is unavailable. It is a well-known compatibility shim.

  9. info warn → info by review dynamic-attribute Lib/fontTools/misc/cython.py:24:5

    reach-by-name through `globals` (ast pass, confidence 0.60)

    for name in ("cfunc", "inline"):
        globals()[name] = _empty_decorator
    
    locals = lambda **_: _empty_decorator
    likely benign LLM, confidence 0.90 Assigns empty decorator stubs for Cython decorators in the pure-Python fallback.
    Reasoning

    Assigns no-op decorator stubs into globals for cfunc and inline when Cython is absent. This is a benign compatibility shim consistent with the paired finding.

  10. info warn → info by review dynamic-attribute Lib/fontTools/misc/loggingTools.py:294:20

    reach-by-name through `locals.get` (ast pass, confidence 0.60)

                for arg in ("msg", "level"):
                    if locals().get(arg) is not None:
                        raise ValueError("'%s' can't be specified without a 'logger'" % arg)
            self.logger = logger
    likely benign LLM, confidence 0.85 Checks local arguments via locals().get to raise a ValueError on invalid argument combinations.
    Reasoning

    Reads the local variables msg and level via locals() to validate that they are not passed without a logger. This is a plain argument check with no hidden execution.

  11. info whitespace-anomaly Lib/fontTools/misc/macRes.py:10:1

    file mixes tab and space indentation (19 tab-indented and 178 space-indented lines) (raw pass, confidence 0.40)

    class ResourceError(Exception):
        pass
  12. info whitespace-anomaly Lib/fontTools/misc/psLib.py:4:1

    file mixes tab and space indentation (9 tab-indented and 320 space-indented lines) (raw pass, confidence 0.40)

    from .psOperators import (
        PSOperators,
        ps_StandardEncoding,
        ps_array,
  13. info whitespace-anomaly Lib/fontTools/misc/sstruct.py:26:1

    file mixes tab and space indentation (13 tab-indented and 129 space-indented lines) (raw pass, confidence 0.40)

    pack(fmt, object):
            'object' is either a dictionary or an instance (or actually
            anything that has a __dict__ attribute). If it is a dictionary,
            its keys are used for names. If it is an instance, it's
  14. info whitespace-anomaly Lib/fontTools/misc/symfont.py:20:1

    file mixes tab and space indentation (40 tab-indented and 113 space-indented lines) (raw pass, confidence 0.40)

    for i in range(1, n + 1):
        last = BinomialCoefficient[-1]
        this = tuple(last[j - 1] + last[j] for j in range(len(last))) + (0,)
        BinomialCoefficient.append(this)
  15. info warn → info by review dynamic-execution Lib/fontTools/misc/symfont.py:239:25

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

            penName = sys.argv[1]
            funcs = [(name, eval(f)) for name, f in zip(sys.argv[2::2], sys.argv[3::2])]
            printGreenPen(penName, funcs, file=sys.stdout)
    likely benign LLM, confidence 0.70 Evaluates user-supplied CLI expression strings to build symbolic functions for a pen.
    Reasoning

    eval is applied to command-line arguments in a developer CLI that turns expression strings into pen functions. This is unsafe by design but the input is supplied locally by the user running the tool, not fetched from a remote source, so it is not an obfuscated dropper.

  16. info warn → info by review dynamic-attribute Lib/fontTools/qu2cu/benchmark.py:38:20

    reach-by-name through `globals` (ast pass, confidence 0.60)

        def wrapper(function, setup_func):
            function = globals()[function]
            setup_func = globals()[setup_func]
    likely benign LLM, confidence 0.90 Resolves a benchmark function name via globals() in the qu2cu benchmark wrapper.
    Reasoning

    Mirrors the cu2qu benchmark wrapper, using globals() to resolve benchmark function names from a fixed set. It is benign dispatch.

  17. info warn → info by review dynamic-attribute Lib/fontTools/qu2cu/benchmark.py:39:22

    reach-by-name through `globals` (ast pass, confidence 0.60)

            function = globals()[function]
            setup_func = globals()[setup_func]
    
            def wrapped():
    likely benign LLM, confidence 0.90 Resolves the setup function name via globals() in the qu2cu benchmark wrapper.
    Reasoning

    Same globals() setup-function lookup as the other benchmark finding; benign dispatch in a benchmark harness.

  18. info whitespace-anomaly Lib/fontTools/ttLib/__main__.py:8:1

    file mixes tab and space indentation (8 tab-indented and 116 space-indented lines) (raw pass, confidence 0.40)

    def main(args=None):
        """Open/save fonts with TTFont() or TTCollection()
    
          ./fonttools ttLib [-oFILE] [-yNUMBER] files...
  19. info whitespace-anomaly Lib/fontTools/ttLib/sfnt.py:32:1

    file mixes tab and space indentation (43 tab-indented and 518 space-indented lines) (raw pass, confidence 0.40)

    class SFNTReader(object):
        def __new__(cls, *args, **kwargs):
            """Return an instance of the SFNTReader sub-class which is compatible
            with the input file type.
  20. info warn → info by review decoder-decompress-payload Lib/fontTools/ttLib/sfnt.py:599:20

    file imports decoder module(s) zlib and calls `.decompress()` — embedded payload decompressed at runtime (ast pass, confidence 0.75)

                    )
                data = zlib.decompress(rawData)
                if len(data) != self.origLength:
                    raise TTLibError(
    likely benign LLM, confidence 0.92 Decompresses zlib-compressed WOFF font table data during parsing.
    Reasoning

    zlib.decompress on raw table data is the standard WOFF table decompression step in a font parser. The data is font content, not an embedded hidden payload executed at runtime.

  21. info warn → info by review dynamic-attribute Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py:49:17

    reach-by-name through `vars` (ast pass, confidence 0.60)

                if name in metricNames:
                    vars(self)[name] = safeEval(attrs["value"])
                else:
                    log.warning(
    likely benign LLM, confidence 0.90 Sets bitmap metric attributes from XML by name, using safeEval on the value.
    Reasoning

    Sets attributes via vars(self) after checking the name against a fixed metric list, and parses values with safeEval. This is standard XML-to-object round-tripping in fontTools.

  22. file mixes tab and space indentation (12 tab-indented and 104 space-indented lines) (raw pass, confidence 0.40)

    if TYPE_CHECKING:
        from fontTools.misc.xmlWriter import XMLWriter
        from fontTools.ttLib import TTFont
  23. file mixes tab and space indentation (6 tab-indented and 642 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.misc.textTools import (
        bytechr,
        byteord,
        bytesjoin,
  24. info warn → info by review dynamic-attribute Lib/fontTools/ttLib/tables/E_B_D_T_.py:233:17

    reach-by-name through `vars` (ast pass, confidence 0.60)

                if name in componentNames:
                    vars(self)[name] = safeEval(attrs["value"])
                else:
                    log.warning("unknown name '%s' being ignored by EbdtComponent.", name)
    likely benign LLM, confidence 0.90 Sets EBDT component attributes from XML by validated name, using safeEval.
    Reasoning

    Assigns attributes via vars(self) after validating the name against known component names, with safeEval for the value. This is ordinary fontTools table deserialization.

  25. file mixes tab and space indentation (28 tab-indented and 544 space-indented lines) (raw pass, confidence 0.40)

    from .BitmapGlyphMetrics import (
        BigGlyphMetrics,
        bigGlyphMetricsFormat,
        SmallGlyphMetrics,
  26. info warn → info by review dynamic-attribute Lib/fontTools/ttLib/tables/E_B_L_C_.py:103:17

    reach-by-name through `vars` (ast pass, confidence 0.60)

                    metricObj = SbitLineMetrics()
                    vars(curTable)[metric] = metricObj
                    dummy = sstruct.unpack2(
                        sbitLineMetricsFormat, data[i : i + 12], metricObj
    likely benign LLM, confidence 0.90 Stores sbit line metric objects on the table via vars() during binary unpacking.
    Reasoning

    Stores line metric objects into the table object via vars() using known metric names from the format definition. This is standard structured-data handling.

  27. info warn → info by review dynamic-attribute Lib/fontTools/ttLib/tables/E_B_L_C_.py:236:29

    reach-by-name through `vars` (ast pass, confidence 0.60)

                for metric in ("hori", "vert"):
                    metricObj = vars(curTable)[metric]
                    data = sstruct.pack(sbitLineMetricsFormat, metricObj)
                    dataList.append(data)
    likely benign LLM, confidence 0.90 Reads sbit line metric attributes via vars() when packing the table.
    Reasoning

    Reads hori and vert metric objects back from the table via vars() for packing. The keys are fixed, so this is benign serialization code.

  28. info warn → info by review dynamic-attribute Lib/fontTools/ttLib/tables/E_B_L_C_.py:339:17

    reach-by-name through `vars` (ast pass, confidence 0.60)

                    metricObj.fromXML(name, attrs, content, ttFont)
                    vars(self)[direction] = metricObj
                elif name in dataNames:
                    vars(self)[name] = safeEval(attrs["value"])
    likely benign LLM, confidence 0.90 Assigns metric objects and data fields via vars() during XML parsing of the table.
    Reasoning

    Assigns direction-keyed metric objects and data names through vars() during XML parsing, with safeEval for values. This is ordinary fontTools table code.

  29. info warn → info by review dynamic-attribute Lib/fontTools/ttLib/tables/E_B_L_C_.py:341:17

    reach-by-name through `vars` (ast pass, confidence 0.60)

                elif name in dataNames:
                    vars(self)[name] = safeEval(attrs["value"])
                else:
                    log.warning("unknown name '%s' being ignored in BitmapSizeTable.", name)
    likely benign LLM, confidence 0.93 Sets a whitelisted BitmapSizeTable attribute from parsed XML via vars(self).
    Reasoning

    The vars(self) assignment is guarded by `name in dataNames`, a fixed whitelist of table attribute names, and the value goes through safeEval. This is ordinary XML-to-object deserialization in a font library.

  30. info warn → info by review dynamic-attribute Lib/fontTools/ttLib/tables/E_B_L_C_.py:363:17

    reach-by-name through `vars` (ast pass, confidence 0.60)

                if name in metricNames:
                    vars(self)[name] = safeEval(attrs["value"])
    likely benign LLM, confidence 0.93 Sets a whitelisted metric attribute from XML via vars(self) when the name is in metricNames.
    Reasoning

    The assignment is gated by `name in metricNames`, a fixed whitelist, and uses safeEval. It matches the adjacent benign XML-reading code.

  31. file mixes tab and space indentation (5 tab-indented and 31 space-indented lines) (raw pass, confidence 0.40)

    FFTMFormat = """
    		>	# big endian
    		version:        I
    		FFTimeStamp:    Q
  32. file mixes tab and space indentation (50 tab-indented and 602 space-indented lines) (raw pass, confidence 0.40)

    panoseFormat = """
    	bFamilyType:        B
    	bSerifStyle:        B
    	bWeight:            B
  33. file mixes tab and space indentation (9 tab-indented and 159 space-indented lines) (raw pass, confidence 0.40)

    
            <SVG>
                    <svgDoc endGlyphID="1" startGlyphID="1">
                            <![CDATA[ <complete SVG doc> ]]
  34. file mixes tab and space indentation (17 tab-indented and 202 space-indented lines) (raw pass, confidence 0.40)

    VDMX_HeaderFmt = """
    	>                 # big endian
    	version:     H    # Version number (0 or 1)
    	numRecs:     H    # Number of VDMX groups present
  35. file mixes tab and space indentation (6 tab-indented and 2012 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.misc.fixedTools import (
        fixedToFloat as fi2fl,
        floatToFixed as fl2fi,
        floatToFixedToStr as fl2str,
  36. file mixes tab and space indentation (9 tab-indented and 275 space-indented lines) (raw pass, confidence 0.40)

    GVAR_HEADER_FORMAT_HEAD = """
    	> # big endian
    	version:			H
    	reserved:			H
  37. file mixes tab and space indentation (4 tab-indented and 98 space-indented lines) (raw pass, confidence 0.40)

    hdmxHeaderFormat = """
    	>   # big endian!
    	version:	H
    	numRecords:	H
  38. file mixes tab and space indentation (18 tab-indented and 86 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.misc.timeTools import (
        timestampFromString,
        timestampToString,
        timestampNow,
  39. file mixes tab and space indentation (18 tab-indented and 98 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.misc.fixedTools import (
        ensureVersionIsLong as fi2ve,
        versionToFixed as ve2fi,
    )
  40. file mixes tab and space indentation (17 tab-indented and 109 space-indented lines) (raw pass, confidence 0.40)

    maxpFormat_0_5 = """
    		>	# big endian
    		tableVersion:           i
    		numGlyphs:              H
  41. file mixes tab and space indentation (7 tab-indented and 1071 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.misc.textTools import (
        bytechr,
        byteord,
        bytesjoin,
  42. file mixes tab and space indentation (10 tab-indented and 259 space-indented lines) (raw pass, confidence 0.40)

    postFormat = """
    	>
    	formatType:			16.16F
    	italicAngle:		16.16F		# italic angle in degrees
  43. file mixes tab and space indentation (13 tab-indented and 79 space-indented lines) (raw pass, confidence 0.40)

    sbixHeaderFormat = """
    	>
    	version:       H	# Version number (set to 1)
    	flags:         H	# The only two bits used in the flags field are bits 0
  44. file mixes tab and space indentation (14 tab-indented and 231 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.misc.fixedTools import (
        fixedToFloat as fi2fl,
        floatToFixed as fl2fi,
        floatToFixedToStr as fl2str,
  45. file mixes tab and space indentation (18 tab-indented and 92 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.misc.fixedTools import (
        ensureVersionIsLong as fi2ve,
        versionToFixed as ve2fi,
    )
  46. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otBase.py:959:37

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

                else:
                    if conv.aux and not eval(conv.aux, None, self.__dict__):
                        continue
                    if hasattr(self, conv.name):
    likely benign LLM, confidence 0.88 Evaluates a per-converter condition expression from the OpenType table schema against the table's attributes.
    Reasoning

    The eval runs conv.aux, a condition expression taken from developer-authored otData table specifications, not from font bytes. The namespace is restricted to the table's own dict.

  47. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otBase.py:997:41

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

                    else:
                        if conv.aux and not eval(conv.aux, None, table):
                            continue
                        table[conv.name] = conv.read(reader, font, table)
    likely benign LLM, confidence 0.88 Evaluates a schema-defined aux condition during binary table reading.
    Reasoning

    Same pattern: eval of a schema-defined conv.aux condition against the table dict. The input is developer-defined spec, not attacker-controlled data.

  48. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otBase.py:1101:37

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

                else:
                    if conv.aux and not eval(conv.aux, None, table):
                        continue
                    try:
    likely benign LLM, confidence 0.88 Evaluates a schema-defined conditional field expression during table compilation.
    Reasoning

    eval of conv.aux from the static OpenType schema, applied to the table namespace. This is the library's standard conditional-field mechanism.

  49. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otBase.py:1143:37

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

                else:
                    if conv.aux and not eval(conv.aux, None, vars(self)):
                        continue
                    value = getattr(
    likely benign LLM, confidence 0.88 Evaluates a schema-defined aux expression against the table's attribute dict.
    Reasoning

    eval of the static schema expression conv.aux against vars(self). The expression source is not derived from font data.

  50. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otConverters.py:87:34

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

                else:
                    converterClass = eval(spec.type, tableNamespace, converterMapping)
    
            conv = converterClass(
    likely benign LLM, confidence 0.88 Resolves an OpenType converter class name from the schema via eval.
    Reasoning

    The eval resolves a converter class from spec.type, which comes from the bundled otData schema. The namespace is fixed in the library, so this is class-name lookup rather than arbitrary execution.

  51. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otConverters.py:134:24

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

            if self.aux and not self.repeat:
                self.aux = compile(self.aux, "<string>", "eval")
            self.tableClass = tableClass
            self.isCount = name.endswith("Count") or name in [
    likely benign LLM, confidence 0.88 Pre-compiles a schema-defined aux expression for later evaluation.
    Reasoning

    compile is applied to self.aux, a schema-defined expression string, in eval mode. The source is trusted library data.

  52. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otTables.py:1741:37

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

                else:
                    if conv.aux and not eval(conv.aux, None, vars(self)):
                        continue
                    value = getattr(
    likely benign LLM, confidence 0.88 Evaluates a schema-defined conditional expression during table compilation.
    Reasoning

    Same schema-driven eval of conv.aux against vars(self) as in otBase. It is a standard, developer-controlled conditional mechanism.

  53. file mixes tab and space indentation (10 tab-indented and 114 space-indented lines) (raw pass, confidence 0.40)

    sbixGlyphHeaderFormat = """
    	>
    	originOffsetX: h	# The x-value of the point in the glyph relative to its
    						# lower-left corner which corresponds to the origin of
  54. file mixes tab and space indentation (8 tab-indented and 137 space-indented lines) (raw pass, confidence 0.40)

    sbixStrikeHeaderFormat = """
    	>
    	ppem:          H	# The PPEM for which this strike was designed (e.g., 9,
    						# 12, 24)
  55. info warn → info by review encoding-octal Lib/fontTools/ttLib/tables/ttProgram.py:582:44

    6 rows of escape runs, lines 582-582 (first: 32 consecutive `\NNN` octal escapes (entropy 5.0 bits/byte)) (raw pass, confidence 0.65)

        bc = b"""@;:9876543210/.-,+*)(\'&%$#"! \037\036\035\034\033\032\031\030\027\026\025\024\023\022\021\020\017\016\015\014\013\012\011\010\007\006\005\004\003\002\001\000,\001\260\030CXEj\260\031C`\260F#D#\020 \260FN\360M/\260\000\022\033!#\0213Y-,\001\260\030CX\260\005+\260\000\023K\260\024PX\261\000@8Y\260\006+\033!
    likely benign LLM, confidence 0.80 A large bytes literal of octal escapes, most likely embedded binary constant data.
    Reasoning

    The raw pass flags a long bytes literal of octal escapes, which is a binary constant table typical of embedded PostScript or glyph-program data in a font library. The snippet shows no decoding or execution, and the pass has a higher false-positive rate.

  56. info warn → info by review dynamic-attribute Lib/fontTools/ttLib/ttFont.py:254:19

    reach-by-name through `locals.get` (ast pass, confidence 0.60)

            for name in ("verbose", "quiet"):
                val = locals().get(name)
                if val is not None:
                    deprecateArgument(name, "configure logging instead")
    likely benign LLM, confidence 0.90 Checks for deprecated verbose/quiet arguments by name via locals().get.
    Reasoning

    locals().get is used over a fixed tuple of argument names ('verbose', 'quiet') to detect deprecated kwargs. This is a benign reflective lookup over a constant set.

  57. info critical → info by review dynamic-import Lib/fontTools/ttLib/ttFont.py:1434:9

    `__import__` called on a non-literal expression (ast pass, confidence 0.90)

        try:
            __import__("fontTools.ttLib.tables." + pyTag)
        except ImportError as err:
            # If pyTag is found in the ImportError message,
    likely benign LLM, confidence 0.72 Dynamically imports a fontTools table module named from the font's table tag.
    Reasoning

    The import is prefixed with a fixed package path and only loads modules under fontTools.ttLib.tables, with ImportError handling for unknown tags. The tag comes from font data, but it can only reach fontTools's own table modules, so the risk is limited. It is still a dynamic import of font-supplied input, so some doubt remains.

  58. info whitespace-anomaly Lib/fontTools/ttLib/woff2.py:10:1

    file mixes tab and space indentation (36 tab-indented and 1383 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.ttLib import (
        TTFont,
        TTLibError,
        getTableModule,
  59. info whitespace-anomaly Lib/fontTools/varLib/cff.py:3:1

    file mixes tab and space indentation (12 tab-indented and 501 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.cffLib import (
        maxStackLimit,
        TopDictIndex,
        buildOrder,
  60. info critical → info by review dynamic-execution MetaTools/roundTrip.py:51:18

    `os.popen` called on a non-literal command — runtime-determined shell invocation (ast pass, confidence 0.85)

            )
            output = os.popen(diffcmd, "r", 1)
            lines = []
            while True:
    likely benign LLM, confidence 0.70 Runs an external diff command via os.popen in a round-trip test helper.
    Reasoning

    This is a developer test utility that runs a diff command via os.popen. The command is built locally from paths, and exploiting it requires control of the local invocation, not a remote payload. Shell use in a dev script is not by itself malicious, but the command's construction is not shown.

  61. line length 555 characters (raw pass, confidence 0.50)

    lookup	slashcontext 	chained⟨U+000D⟩⟨U+000D⟩RightToLeft	no⟨U+000D⟩IgnoreBaseGlyphs	no⟨U+000D⟩IgnoreLigatures	no⟨U+000D⟩IgnoreMarks	no⟨U+000D⟩⟨U+000D⟩backtrackcoverage definition begin⟨U+000D⟩zero⟨U+000D⟩one⟨U+000D⟩two⟨U+000D⟩three⟨U+000D⟩four⟨U+000D⟩five⟨U+000D⟩six⟨U+000D⟩seven⟨U+000D⟩eight⟨U+000D⟩nine⟨U+000D⟩coverage definition end⟨U+000D⟩⟨U+000D⟩inputcoverage definition begin⟨U+000D⟩slash⟨U+000D⟩coverage definition end⟨U+000D⟩⟨U+000D⟩lookaheadcoverage definition begin⟨U+000D⟩zero⟨U+000D⟩one⟨U+000D⟩two⟨U+000D⟩
  62. line length 708 characters (raw pass, confidence 0.50)

    ⟨U+000D⟩lookup	i-ligatures-deva	context⟨U+000D⟩⟨U+000D⟩class definition begin⟨U+000D⟩ivowelsigndeva	1⟨U+000D⟩ivowelsign1deva	1⟨U+000D⟩ivowelsign2deva	1⟨U+000D⟩anusvaradeva	2⟨U+000D⟩candrabindudeva	3⟨U+000D⟩rephdeva	4⟨U+000D⟩rephanusvaradeva	5⟨U+000D⟩class definition end⟨U+000D⟩⟨U+000D⟩class	1,0,0,0,2	1,i-anusvara-deva	5,removemark-deva⟨U+000D⟩class	1,0,0,0,4	1,i-reph-deva	5,removemark-deva⟨U+000D⟩class	1,0,0,0,5	1,i-repha
  63. line length 555 characters (raw pass, confidence 0.50)

    lookup	slashcontext 	context⟨U+000D⟩⟨U+000D⟩RightToLeft	no⟨U+000D⟩IgnoreBaseGlyphs	no⟨U+000D⟩IgnoreLigatures	no⟨U+000D⟩IgnoreMarks	no⟨U+000D⟩⟨U+000D⟩coverage definition begin	 0⟨U+000D⟩zero⟨U+000D⟩one⟨U+000D⟩two⟨U+000D⟩three⟨U+000D⟩four⟨U+000D⟩five⟨U+000D⟩six⟨U+000D⟩seven⟨U+000D⟩eight⟨U+000D⟩nine⟨U+000D⟩coverage definition end⟨U+000D⟩⟨U+000D⟩coverage definition begin	 1⟨U+000D⟩slash⟨U+000D⟩coverage definition end⟨U+000D⟩⟨U+000D⟩coverage definition begin	 2⟨U+000D⟩zero⟨U+000D⟩one⟨U+000D⟩two⟨U+000D⟩three⟨U+000D⟩four⟨U+000D⟩fiv
  64. line length 904 characters (raw pass, confidence 0.50)

    lookup	topmarktobase-guru	mark to base⟨U+000D⟩⟨U+000D⟩mark	bindigurmukhi	0	-184, 1183	 16⟨U+000D⟩base	ngagurmukhi	0	 816, 1183	 41⟨U+000D⟩base	tthagurmukhi	0	 816, 1183	 30⟨U+000D⟩base	nnagurmukhi	0	 976, 1183	 35⟨U+000D⟩base	nagurmukhi	0	 816, 1183	 32⟨U+000D⟩base	lagurmukhi	0	 996, 1183	 46⟨U+000D⟩base	lanuktagurmukhi	0	 996, 1183	 46⟨U+000D⟩mark	eematragurmukhi	0	-184, 1183	 15
  65. line length 1031 characters (raw pass, confidence 0.50)

    ⟨U+000D⟩lookup	 supsToInferiors 	single⟨U+000D⟩⟨U+000D⟩RightToLeft	no⟨U+000D⟩IgnoreBaseGlyphs	no⟨U+000D⟩IgnoreLigatures	no⟨U+000D⟩IgnoreMarks	no⟨U+000D⟩⟨U+000D⟩y placement	asuperior	-560⟨U+000D⟩y placement	bsuperior	-560⟨U+000D⟩y placement	csuperior	-560⟨U+000D⟩y placement	dsuperior	-560⟨U+000D⟩y placement	esuperior	-560⟨U+000D⟩y placement	fsuperior	-560⟨U+000D⟩y placement	gsuperior	-560⟨U+000D⟩y placement	hsuperior	-560⟨U+000D⟩
  66. line length 602 characters (raw pass, confidence 0.50)

    lookup	arabicReverse	reversechained⟨U+000D⟩⟨U+000D⟩RightToLeft	yes⟨U+000D⟩IgnoreBaseGlyphs	no⟨U+000D⟩IgnoreLigatures	no⟨U+000D⟩IgnoreMarks	yes⟨U+000D⟩⟨U+000D⟩⟨U+000D⟩backtrackcoverage definition begin⟨U+000D⟩bayi1⟨U+000D⟩jeemi1⟨U+000D⟩kafi1⟨U+000D⟩ghafi1⟨U+000D⟩laami1⟨U+000D⟩kafm1⟨U+000D⟩ghafm1⟨U+000D⟩laamm1⟨U+000D⟩coverage definition end⟨U+000D⟩⟨U+000D⟩rayf2	rayf1⟨U+000D⟩reyf2	reyf1⟨U+000D⟩zayf2	zayf1⟨U+000D⟩yayf2	yayf1⟨U+000D⟩⟨U+000D⟩% subtable⟨U+000D⟩⟨U+000D⟩backtrackcoverage definition begin⟨U+000D⟩b
  67. info warn → info by review dynamic-execution Tests/ttLib/tables/TupleVariation_test.py:579:27

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

                    points,
                    decompile(compile(points)),
                    "failed round-trip decompile/compilePoints; points=%s" % points,
                )
    likely benign LLM, confidence 0.93 Test assertion that round-trips point data through a compile/decompile method.
    Reasoning

    The call is compile(points), which is almost certainly the fontTools TupleVariation compile method inside a test assertion, not Python's builtin compile on dynamic code. This is a name collision with a legitimate test pattern.

  68. info warn → info by review dynamic-execution Tests/ttLib/tables/TupleVariation_test.py:583:50

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

            allPoints = set(range(numPointsInGlyph))
            self.assertSetEqual(allPoints, decompile(compile(allPoints)))
            self.assertSetEqual(allPoints, decompile(compile(set())))
    likely benign LLM, confidence 0.85 Test calling fontTools' compile helper on a locally built set of point indices.
    Reasoning

    The call is to fontTools' own TupleVariation compile function, which serializes point sets to bytes. It is not Python's builtin compile on attacker input. The file is a unit test round-tripping known in-test data, with no network, shell, or dropper context.

  69. info warn → info by review dynamic-execution Tests/ttLib/tables/TupleVariation_test.py:584:50

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

            self.assertSetEqual(allPoints, decompile(compile(allPoints)))
            self.assertSetEqual(allPoints, decompile(compile(set())))
    
        def test_compileDeltas_points(self):
    likely benign LLM, confidence 0.85 Round-trip test of fontTools point-set compile/decompile with a constant empty set.
    Reasoning

    Same pattern as the adjacent finding: a fontTools serialization helper applied to a locally constructed set in a round-trip assertion. Nothing indicates obfuscation or hidden execution.

  70. info warn → info by review dynamic-execution Tests/ttLib/tables/TupleVariation_test.py:730:52

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

                random.shuffle(deltas)
                self.assertListEqual(deltas, decompile(compile(deltas)))
    
        def test_compileSharedTuples(self):
    likely benign LLM, confidence 0.85 Round-trip test of fontTools delta compilation over shuffled test data.
    Reasoning

    The argument is a list of test deltas shuffled locally, passed to fontTools' compile for a round-trip assertion. The flagged name matches the library function, not Python's builtin compile.

  71. info unicode-mixed-script Tests/ttLib/tables/_n_a_m_e_test.py:314:51

    identifier `ⱾƤℚⱤ` mixes scripts: Other + Latin (in string/comment) (raw pass, confidence 0.40)

            with CapturingLogHandler(log, "WARNING") as captor:
                nameTable.addMultilingualName({"la": "ⱾƤℚⱤ"}, ttFont=font)
            captor.assertRegex("cannot add Windows name in language la")
            self.assertEqual(names(nameTable), [(256, 0, 4, 0, "ⱾƤℚⱤ")])
  72. info unicode-mixed-script Tests/ttLib/tables/_n_a_m_e_test.py:316:61

    identifier `ⱾƤℚⱤ` mixes scripts: Other + Latin (in string/comment) (raw pass, confidence 0.40)

            captor.assertRegex("cannot add Windows name in language la")
            self.assertEqual(names(nameTable), [(256, 0, 4, 0, "ⱾƤℚⱤ")])
            self.assertIn("ltag", font.tables)
            self.assertEqual(font["ltag"].tags, ["la"])
  73. info unicode-mixed-script Tests/ttLib/tables/_n_a_m_e_test.py:378:62

    identifier `ⱾƤℚⱤ` mixes scripts: Other + Latin (in string/comment) (raw pass, confidence 0.40)

            with CapturingLogHandler(log, "WARNING") as captor:
                nameTable.addMultilingualName({"en": "A", "la": "ⱾƤℚⱤ"})
            captor.assertRegex("cannot store language la into 'ltag' table")
  74. info warn → info by review dynamic-attribute Tests/ttLib/tables/tables_test.py:287:16

    reach-by-name through `globals` (ast pass, confidence 0.60)

            for req in TEST_REQUIREMENTS[testfile]:
                if globals()[req] is None:
                    pytest.skip("%s not installed" % req)
    likely benign LLM, confidence 0.88 Test helper that checks optional dependency availability via globals() and skips when missing.
    Reasoning

    The test looks up a module-level name from a fixed requirements list to check whether an optional dependency is installed, then skips if it is missing. This is a common test-harness pattern with no obfuscation or malicious payload.

  75. info whitespace-anomaly Tests/ufoLib/GLIF1_test.py:3:1

    file mixes tab and space indentation (820 tab-indented and 461 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.ufoLib.glifLib import (
        GlifLibError,
        readGlyphFromString,
        writeGlyphToString,
  76. info warn → info by review dynamic-execution Tests/ufoLib/GLIF1_test.py:26:9

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

            glyph = Glyph()
            exec(py, {"glyph": glyph, "pointPen": glyph})
            glif = writeGlyphToString(
                glyph.name,
    likely benign LLM, confidence 0.85 Test executing a drawing-command snippet to populate a glyph for GLIF1 serialization checks.
    Reasoning

    The exec runs a fixed in-repo drawing-command snippet against a local Glyph object and pointPen, which is a standard way to test glif point-pen output. It is a test fixture, not a dropper, and the code is not encoded or fetched remotely.

  77. info whitespace-anomaly Tests/ufoLib/GLIF2_test.py:3:1

    file mixes tab and space indentation (1591 tab-indented and 704 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.ufoLib.glifLib import (
        GlifLibError,
        readGlyphFromString,
        writeGlyphToString,
  78. info warn → info by review dynamic-execution Tests/ufoLib/GLIF2_test.py:26:9

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

            glyph = Glyph()
            exec(py, {"glyph": glyph, "pointPen": glyph})
            glif = writeGlyphToString(
                glyph.name,
    likely benign LLM, confidence 0.85 Test executing a drawing-command snippet to build a glyph for GLIF2 serialization checks.
    Reasoning

    This mirrors the GLIF1 test: exec of a local, in-repo drawing snippet against a Glyph and pointPen for GLIF2 serialization testing. It is test scaffolding with no malicious indicators.

  79. info whitespace-anomaly Tests/ufoLib/glifLib_test.py:14:1

    file mixes tab and space indentation (26 tab-indented and 290 space-indented lines) (raw pass, confidence 0.40)

    from fontTools.ufoLib.errors import (
        GlifLibError,
        UnsupportedGLIFFormat,
        UnsupportedUFOFormat,
  80. info warn → info by review dynamic-execution Tests/ufoLib/testSupport.py:61:13

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

                py = "\n".join(self.outline)
                exec(py, {"pointPen": pointPen})
    
        def py(self):
    likely benign LLM, confidence 0.85 Test support helper executing fixture outline code to drive a point pen.
    Reasoning

    This is a test helper that execs the test's own outline source lines with a restricted namespace to drive a pen. The input comes from the test fixture itself, not from an external or encoded source.

Reproduce This Scan

Run disclude locally to verify. Add --format sarif for SARIF output. Add --llm (with an API key in the environment) to include the LLM review.

cargo install disclude --version 4.1.0
git clone https://github.com/fonttools/fonttools fonttools && cd fonttools
git checkout 4.67.0
disclude scan --severity info --diff 4.66.1 .