PyPI / fonttools / 4.66.1

fonttools 4.66.1

Source
https://github.com/fonttools/fonttools
Tag scanned
4.66.1
Compared with
4.66.0 (0 signals introduced since)
Files
820 scanned, 1813 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 80 info
Scan time
31.5 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 36 of 36 warn and critical signals; 36 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-14 18:39

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 3115 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.93 Benchmark wrapper resolves function names through globals() to time them.
    Reasoning

    The wrapper looks up a benchmark function and its setup function by name in module globals. This is a standard way to pick functions for a timing harness from command-line or configuration names, and the names come from the benchmark's own 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.93 Second globals() name lookup in the same benchmark wrapper.
    Reasoning

    Same pattern as the adjacent finding: the inner wrapper resolves the setup and benchmark functions by name in module globals within a benchmarking helper. No external or untrusted input is involved.

  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.85 help.py dynamically imports fontTools submodules by name to collect their main() entry points.
    Reasoning

    __import__ is called with a loop variable pkg. In fontTools' help module this iterates over a fixed set of fontTools submodule names to print their help, so the import target is internal and bounded. The critical severity overstates a benign dynamic import.

  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.93 Cython compatibility shim that stubs out Cython type names in module globals.
    Reasoning

    This is a fallback shim: when Cython is unavailable, it sets the names double, complex, int and similar to None in globals so that annotated pure-Python code still runs. The pattern is a known compatibility technique and is fully visible in the snippet.

  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.93 Cython fallback binds cfunc and inline to a no-op decorator via globals().
    Reasoning

    Continuation of the same no-op Cython fallback: decorator names such as cfunc and inline are bound to an empty decorator when Cython is missing. This is legitimate compatibility code.

  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.90 Argument-validation check reads local variables via locals().get.
    Reasoning

    locals().get is used only to check whether the msg or level arguments were supplied, in order to raise a ValueError. This is ordinary argument validation and has no effect beyond reading local variables.

  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 (12 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 Command-line helper evals user-supplied expression strings to construct symbolic pen functions.
    Reasoning

    eval is applied to command-line arguments to build pen functions for symbolic font output. The input is supplied by the local user running the script, not fetched from a remote or hidden source, so it is a risky but expected debugging-tool design. The eval is a real code-execution path, so it is not dismissed outright.

  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.93 Benchmark wrapper resolves function names through globals().
    Reasoning

    Same benchmark-harness name lookup through globals() as in the cu2qu benchmark, with no external input or obfuscation.

  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.93 Second globals() name lookup in the qu2cu benchmark wrapper.
    Reasoning

    Second globals() name lookup in the qu2cu benchmark wrapper, matching the benign benchmarking pattern.

  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.90 Decompresses a zlib-compressed WOFF table during font parsing.
    Reasoning

    zlib.decompress is used on WOFF table data, which is compressed by specification. The result is checked against origLength. This is the expected decoding of a standard font container format, not a hidden payload.

  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.92 XML importer stores whitelisted metric attributes via vars(self) after safeEval.
    Reasoning

    During XML import, the attribute value is parsed with safeEval and stored in vars(self) under a name first checked against a fixed metricNames set. The safe-eval and whitelist make this a normal deserialization path.

  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.92 XML importer sets whitelisted component attributes via vars(self) after safeEval.
    Reasoning

    Same XML-import pattern: the name is checked against componentNames and the value goes through safeEval before being set on the object. This is standard 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 Table loader assigns parsed line-metric objects to attributes via vars(curTable).
    Reasoning

    The metric name is part of a known set, and the object is built from the SbitLineMetrics format and sstruct unpacking. Assigning through vars(curTable) is ordinary structured-table handling with no network or shell activity.

  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.93 Reads the 'hori' and 'vert' metric attributes of a bitmap table object by name for packing.
    Reasoning

    vars(curTable)[metric] reads a fixed attribute name from a hardcoded tuple ('hori', 'vert') and passes it to sstruct.pack. This is ordinary attribute access in a font-table serializer, with no external input controlling the name.

  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.92 Sets bitmap-size table attributes from parsed XML elements whose names are validated against known lists.
    Reasoning

    During XML import, the name is either a known direction key or a name from the fixed dataNames list, and the value goes through safeEval. Unknown names are logged and ignored, so this is a controlled setattr pattern in a font-table parser.

  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.92 Assigns data-named attributes from XML values through safeEval, ignoring unknown names.
    Reasoning

    The name is checked against dataNames before assignment, and values are parsed with safeEval rather than eval. Unknown names are discarded with a warning. This is standard fontTools XML round-trip code.

  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.92 Sets metric attributes from XML, guarded by a fixed name list and safeEval.
    Reasoning

    Same pattern: the name is guarded by membership in metricNames and the value goes through safeEval. Routine XML deserialization for a font table.

  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 201 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 1982 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 1064 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:954: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.85 Evaluates a conditional expression from the OpenType table spec to decide whether a field is present.
    Reasoning

    eval evaluates conv.aux, a condition string compiled from fontTools' own OpenType table specifications (otData), not from untrusted input. This is a deliberate design for conditional fields in a font-format library, though eval is a broad primitive.

  47. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otBase.py:992: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.85 Evaluates a spec-defined conditional aux expression during binary font table decoding.
    Reasoning

    Same as the other otBase eval: conv.aux comes from the trusted internal OpenType spec and is evaluated against the table dictionary during binary read. Benign design, though it uses eval.

  48. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otBase.py:1096: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.85 Evaluates a spec-defined conditional expression while compiling an OpenType table.
    Reasoning

    The eval input is the spec-defined conv.aux expression from the internal otData definitions. It runs during table compilation and is not driven by attacker input.

  49. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otBase.py:1138: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.85 Evaluates a spec-defined aux condition against the object's attributes.
    Reasoning

    Same pattern: conv.aux is from the internal OpenType table specification and is evaluated against the object's attributes during XML/object handling. Legitimate, though eval is a broad primitive.

  50. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otConverters.py:83: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.85 Resolves a converter class from the internal OpenType spec type string.
    Reasoning

    spec.type is a converter class name from the internal otData table specification, resolved in a controlled namespace. This is how fontTools maps spec strings to converter classes, not a payload path.

  51. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otConverters.py:130: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.87 Compiles a spec-defined aux condition string into a code object for later evaluation.
    Reasoning

    compile() is applied to self.aux, which comes from the internal OpenType spec, and is used only as a condition expression. Developer-defined and standard for this library.

  52. info warn → info by review dynamic-execution Lib/fontTools/ttLib/tables/otTables.py:1727: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.85 Evaluates a spec-defined aux condition against the table's attributes.
    Reasoning

    Same as the other eval calls on conv.aux: the expression comes from the trusted OpenType spec and is evaluated against the table's attributes.

  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 bytes literal of TrueType instruction bytes written as octal escapes in the font program module.
    Reasoning

    The raw pass flags a long bytes literal of octal escapes. It is TrueType bytecode, which is expected in a font-instruction module, likely test or reference data for the disassembler. Raw-pass findings carry a high false-positive rate, and nothing here executes or decodes a payload.

  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 Reads the deprecated verbose and quiet arguments through locals() to warn about them.
    Reasoning

    locals() is read only to pick up deprecated keyword arguments (verbose, quiet) from a fixed tuple of names. This is ordinary argument-handling code with no execution or hidden behavior.

  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.80 Dynamically imports a fontTools table module named by a four-character table tag.
    Reasoning

    The import name is a fixed prefix plus a table tag, and the ImportError is caught to handle unknown tables. The import is confined to the fontTools.ttLib.tables namespace, which is a standard plugin-loading pattern in fontTools. The tag comes from font data, so a crafted tag can only select an existing module in that package.

  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 a diff command through os.popen in a round-trip test utility.
    Reasoning

    This is a developer test script that shells out to a diff command assembled at runtime. It is a genuine shell call, so it is not a pure false positive, but it is a dev utility rather than a dropper, and nothing in the snippet shows untrusted input reaching the command.

  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,
                )
    dismissed LLM, confidence 0.90 Calls the fontTools TupleVariation compile method for a round-trip test, not the builtin compile.
    Reasoning

    The matched call is decompile(compile(points)), where compile is the fontTools TupleVariation method for serializing point sets. It is not Python's builtin compile, so the rule matched on a name alone.

  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())))
    dismissed LLM, confidence 0.90 Serializes point sets through the fontTools compile method in a unit test.
    Reasoning

    This is the fontTools compile method serializing point sets in a unit test. It is not dynamic code evaluation.

  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):
    dismissed LLM, confidence 0.90 Serializes an empty set through the fontTools compile method in a unit test.
    Reasoning

    Same pattern as the other compile matches: a fontTools serialization method exercised by a test, matched by name only.

  70. info warn → info by review dynamic-execution Tests/ttLib/tables/TupleVariation_test.py:711: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):
    dismissed LLM, confidence 0.90 Serializes delta lists through the fontTools compile method in a round-trip test.
    Reasoning

    The matched call is the fontTools compile method applied to a list of deltas in a test. It is not builtin compile and does not evaluate strings.

  71. info unicode-mixed-script Tests/ttLib/tables/_n_a_m_e_test.py:300: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:302: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:329: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.90 Looks up optional test requirements in globals() to skip tests when a module is missing.
    Reasoning

    globals() is indexed with requirement names from a fixed TEST_REQUIREMENTS table to decide whether to skip a test. This is standard pytest skip logic with no hidden behavior.

  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.80 Executes test fixture outline code to draw a glyph in a GLIF1 test.
    Reasoning

    exec runs Python outline source from a test fixture against a restricted namespace containing only a glyph and a pen. It is a test harness for drawing glyphs, and the source comes from literal test data in the repository.

  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.80 Executes test fixture outline code to draw a glyph in a GLIF2 test.
    Reasoning

    Same harness as the GLIF1 test: exec of repository-literal test outline code against a glyph and pen namespace.

  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.80 Executes outline text from a test helper against a point pen.
    Reasoning

    This test helper execs outline text joined from literal test data, with only a point pen in scope. It is a test utility and the input is not attacker-controlled.

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.0.0
git clone https://github.com/fonttools/fonttools fonttools && cd fonttools
git checkout 4.66.1
disclude scan --severity info --diff 4.66.0 .