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
36.9 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-15 07:26

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

    Looks up benchmark function names from globals() inside a decorator wrapper. This is a common, ordinary way to resolve a named function in a benchmark helper, not obfuscation.

  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 same benchmark wrapper.
    Reasoning

    Same globals() name lookup as the neighbouring finding, in a benchmark helper. The pattern is a plain name-to-function lookup with no sign of hidden payload or intent.

  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 Dynamic import of fontTools tool modules during help discovery, with ImportError ignored.
    Reasoning

    __import__ is called with a loop variable naming fontTools tool packages, and ImportError is caught and skipped. This is plugin-style discovery of the package's own submodules. It is flagged critical only because the argument is non-literal.

  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 Fallback stubs for Cython type names installed into module globals.
    Reasoning

    Assigns None into globals() for a fixed tuple of names (double, complex, int) as a fallback when Cython is absent. This is a standard 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 No-op Cython decorator stubs installed into globals.
    Reasoning

    Installs no-op decorator stubs into globals() for cfunc and inline when Cython is unavailable. This is a standard no-Cython fallback.

  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 Checks locals() for argument names to validate constructor arguments.
    Reasoning

    Reads locals() for fixed argument names to validate that msg and level are not passed without a logger. This is ordinary argument checking.

  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 CLI helper evals user-supplied expression strings from sys.argv.
    Reasoning

    eval is applied to command-line arguments in a developer CLI that builds pen-drawing expressions. It is a real code-execution path for whoever runs the tool, but it is intended behaviour for a local utility and does not look like hidden or supply-chain malice.

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

    Same globals() name lookup pattern as the cu2qu benchmark, in a benchmark helper. Benign.

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

    Second globals() name lookup in the same benchmark wrapper. Benign.

  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.95 Decompresses WOFF table data with zlib during font parsing.
    Reasoning

    zlib.decompress on table data in the sfnt/WOFF reader is the standard decompression step for WOFF-compressed tables, with a length check that follows. Expected for a font library.

  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 XML import sets whitelisted metric attributes via vars() using safeEval values.
    Reasoning

    In the XML fromXML path, it sets an attribute on self by name only after checking the name against a whitelist, with values parsed by safeEval. This is controlled, expected deserialization.

  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 XML import sets whitelisted component attributes via vars() using safeEval values.
    Reasoning

    Same whitelisted-name vars() assignment with safeEval, in the EBDT XML reader. Benign.

  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 Binary table parser stores metric objects via vars() by name.
    Reasoning

    Binary unpacking assigns a metric object onto a table via vars() for a fixed metric name. This is ordinary table-parsing code.

  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 Packs fixed hori/vert metric objects read through vars().
    Reasoning

    Reads metric objects from vars() for the fixed names hori and vert while packing the table. Benign.

  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 XML import stores direction metrics and whitelisted data fields via vars().
    Reasoning

    XML import stores direction metrics and whitelisted data names via vars() with safeEval values. Benign.

  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.90 Whitelist-guarded setattr-style assignment while reading EBLC bitmap size table XML.
    Reasoning

    vars(self)[name] is assigned only after `name in dataNames`, a fixed whitelist of attribute names, and the value goes through safeEval. This is ordinary XML-to-object deserialization in fontTools, not an attacker-controlled attribute write.

  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.90 Whitelist-guarded attribute assignment from XML metric values.
    Reasoning

    The write is guarded by `name in metricNames`, a fixed set of metric field names, and the value goes through safeEval. This is the same benign XML deserialization pattern.

  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.85 Evaluates a schema-defined condition expression while reading OpenType data.
    Reasoning

    conv.aux is a condition expression from fontTools' built-in OpenType table definitions (otData), not from the font file. The eval is a documented design choice of the table schema, so the input is trusted.

  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.85 Evaluates a schema-defined condition expression during table reading.
    Reasoning

    Same as the other otBase eval: conv.aux comes from the built-in otData schema, not untrusted input, so this is a legitimate table-driven condition.

  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.85 Evaluates a schema-defined condition expression during table decompilation.
    Reasoning

    conv.aux is a built-in OpenType schema expression, and the eval is part of the table-conditional logic of the library.

  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.85 Evaluates a schema-defined condition expression during table compilation.
    Reasoning

    Same pattern: the eval expression is a trusted otData schema condition, not data from the font file.

  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.85 Resolves a converter class from a schema type string via eval.
    Reasoning

    spec.type is a converter type string from the built-in OpenType schema, evaluated in a controlled namespace. The input is not attacker-controlled, so this is standard fontTools schema dispatch.

  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.85 Precompiles a schema-defined condition expression for converters.
    Reasoning

    compile() is applied to conv.aux, a schema-defined condition expression from the built-in tables. It is a precompilation step, not runtime injection of untrusted code.

  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.85 Evaluates a schema-defined condition expression for OpenType table fields.
    Reasoning

    Same trusted otData schema condition evaluated with vars(self), consistent with the rest of the OpenType table machinery.

  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.75 Static bytes literal with octal escape sequences in TrueType program code.
    Reasoning

    The flagged item is a large bytes literal of octal escapes, a static data table in the source. Raw-pass byte-level patterns have a high false-positive rate, and a literal constant like this is not an executed payload or dropper.

  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.85 Reads fixed local argument names to emit a deprecation warning.
    Reasoning

    locals().get is used to read the verbose/quiet arguments so a deprecation warning can be emitted. It only reads two fixed names and does not reach arbitrary state.

  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 the table module matching a font table tag.
    Reasoning

    The import is prefixed with a fixed package path, so a tag from the font can only load modules under fontTools.ttLib.tables and cannot escape to arbitrary modules. Tag-to-module-name mapping is a standard fontTools pattern. The critical severity overstates the risk, though a malicious tag could still trigger import of an unexpected in-package module.

  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 shell command via os.popen in a MetaTools round-trip test script.
    Reasoning

    This is a developer test-harness script that runs a diff command to compare round-trip output. The command is built from local file paths, so there is a theoretical shell-injection concern if paths are malicious, but nothing indicates malicious intent or an obfuscated payload.

  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 Unit test calling a library method named compile on point data.
    Reasoning

    The `compile` here is fontTools' own TupleVariation.compilePoints helper in a unit test, not Python's builtin compile on an untrusted string. The flag comes from name matching 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 Test calls a fonttools compile() helper to serialize a set of point indices and checks that decompile() restores it.
    Reasoning

    The call sits in a round-trip unit test where compile() is the fonttools TupleVariation point-packing function, not the Python builtin. The argument is a set of point indices built in the test. Nothing is executed, and the scanner flagged the name alone.

  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 Second round-trip assertion using the same fonttools compile()/decompile() helpers on an empty set.
    Reasoning

    Same pattern as the neighbouring finding: a fonttools serialization helper is round-tripped in a unit test on locally built data. The builtin-compile reading does not apply, so this is a false positive from the name match.

  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):
    dismissed LLM, confidence 0.90 Test round-trips a shuffled deltas list through the fonttools compile/decompile helpers.
    Reasoning

    A fonttools delta-compile helper is applied to a locally shuffled list in a test and then decompiled to check the round trip. It is a data serialization test, not dynamic code execution.

  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.85 Test checks whether optional dependencies are importable via a globals() lookup and skips if any is missing.
    Reasoning

    The test looks up optional-requirement flags by name in globals() to decide whether to skip a test when a module is missing. The keys come from a fixed TEST_REQUIREMENTS table in the test file. This is a common, benign pytest idiom.

  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 GLIF1 test execs a fixture drawing script against a Glyph and point pen to generate expected glif output.
    Reasoning

    exec runs Python source that is part of the test fixture, here a drawing script for a glyph. The namespace is limited to the test's own glyph and pen objects. The snippet shows no network, file, or shell activity. It is a test harness, though exec is still a sensitive primitive.

  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 GLIF2 test execs a fixture drawing script against a Glyph and point pen to build a glyph for comparison.
    Reasoning

    This is the GLIF2 counterpart of the GLIF1 finding. It runs fixture drawing code in a restricted namespace to build a glyph for serialization comparison. Nothing in the snippet is malicious.

  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 Test helper execs a joined outline script against a point pen to produce glyph drawing calls.
    Reasoning

    A test support helper joins a list of outline lines and execs them with a point pen in the namespace. The source is the test's own outline data, not external input. This is a test-fixture pattern, though exec is worth a note.

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.67.0
disclude scan --severity info --diff 4.66.1 .