A Flash Player emulator written in Rust
This report summarizes scanner evidence for ruffle-rs/ruffle, a Rust project with 18,781 GitHub stars. SynthScan examined 967,581 lines of code across 12695 source files, recording 1546 pattern matches distributed across 12 syntactic categories. The legacy adjusted pattern signal is 1.6 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 1495 finding(s).
Are imports declared and package/API names verified? 48 finding(s).
Are wrappers, guards, and repeated structures necessary? 14 finding(s).
Which changes need focused human review? 1546 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 1546 distinct pattern matches across 12 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | deny.toml | 61 | { name = "tracy-client", deny-multiple-versions = true }, | COMMENT |
| LOW | video/software/src/decoder.rs | 21 | /// Preload a frame. | COMMENT |
| LOW | video/external/src/decoder.rs | 1 | // cargo install bindgen-cli | COMMENT |
| LOW | video/external/src/decoder/openh264_sys.rs | 101 | #[doc = "< Intra Refresh in predifined 2% MB"] | COMMENT |
| LOW | video/external/src/decoder/openh264_sys.rs | 141 | pub type SliceInfo = SliceInformation; | COMMENT |
| LOW | video/external/src/decoder/openh264_sys.rs | 641 | #[doc = "< application type; please refer to the definition of EUsageType"] | COMMENT |
| LOW | video/external/src/decoder/openh264_sys.rs | 741 | #[doc = "< get V PSNR stats for the whole video sequence"] | COMMENT |
| LOW | video/external/src/decoder/openh264_sys.rs | 861 | } | COMMENT |
| LOW | video/external/src/decoder/openh264_sys.rs | 901 | #[doc = "< max-cpb"] | COMMENT |
| LOW | video/external/src/decoder/openh264_sys.rs | 1021 | #[doc = "< level idc according to Annex A-1"] | COMMENT |
| LOW | video/src/backend.rs | 1 | use crate::VideoStreamHandle; | COMMENT |
| LOW | video/src/backend.rs | 41 | /// video data should be inspected to determine inter-frame dependencies | COMMENT |
| LOW | video/src/backend.rs | 61 | /// Frames may be decoded in any order that does not violate the frame | COMMENT |
| LOW | core/build_playerglobal/src/avm2.rs | 21 | // should be of the form `[Ruffle(key1 = value1, key2 = value2)]` | COMMENT |
| LOW | core/build_playerglobal/src/avm2.rs | 441 | COMMENT | |
| LOW | core/build_playerglobal/src/avm2.rs | 721 | pub mod #mod_name { | COMMENT |
| LOW | core/build_playerglobal/src/avm2.rs | 741 | // If so, we replace it with a `NativeMethod` constructed | COMMENT |
| LOW | core/build_playerglobal/src/avm2.rs | 761 | COMMENT | |
| LOW | core/macros/src/lib.rs | 21 | /// | COMMENT |
| LOW | core/macros/src/lib.rs | 381 | /// assumed to be in scope and will be used to retrieve the interned string. Otherwise, the | COMMENT |
| LOW | core/common/src/tag_utils.rs | 101 | /// header declares. | COMMENT |
| LOW | core/common/src/duration.rs | 1 | //! A custom Duration type for representing time intervals with f64 precision. | COMMENT |
| LOW | core/common/src/duration.rs | 41 | COMMENT | |
| LOW | core/common/src/buffer.rs | 1 | //! Shared-ownership buffer types | COMMENT |
| LOW | core/common/src/buffer.rs | 241 | UpperHex::fmt(&self.data(), f) | COMMENT |
| LOW | core/common/src/sandbox.rs | 1 | //! Security Sandbox implementation, see | COMMENT |
| LOW | core/common/src/utils.rs | 1 | use gc_arena::{Collect, Gc}; | COMMENT |
| LOW | core/src/frame_lifecycle.rs | 1 | //! Frame events management | COMMENT |
| LOW | core/src/frame_lifecycle.rs | 21 | /// Which phase of the frame we're currently in. | COMMENT |
| LOW | core/src/frame_lifecycle.rs | 41 | /// | COMMENT |
| LOW | core/src/frame_lifecycle.rs | 101 | broadcast_frame_exited(context); | COMMENT |
| LOW | core/src/vminterface.rs | 1 | //! Any-AVM object references | COMMENT |
| LOW | core/src/vminterface.rs | 21 | /// situations in which one might flag a VM action as movie-triggered | COMMENT |
| LOW | core/src/vminterface.rs | 41 | /// objects should be created as AVM2 objects and any custom constructors | COMMENT |
| LOW | core/src/vminterface.rs | 61 | /// | COMMENT |
| LOW | core/src/events.rs | 101 | Self::Handled | COMMENT |
| LOW | core/src/events.rs | 121 | /// * AVM2 delivers broadcasts to all registered `EventDispatcher`s | COMMENT |
| LOW | core/src/events.rs | 161 | /// | COMMENT |
| LOW | core/src/events.rs | 181 | COMMENT | |
| LOW | core/src/events.rs | 201 | COMMENT | |
| LOW | core/src/events.rs | 221 | /// AVM1 and AVM2. The target of this event is determined by the position | COMMENT |
| LOW | core/src/events.rs | 241 | /// Mouse moved out of a display object. | COMMENT |
| LOW | core/src/events.rs | 281 | /// Middle mouse button was released inside a previously-pressed display object. | COMMENT |
| LOW | core/src/events.rs | 501 | } | COMMENT |
| LOW | core/src/orphan_manager.rs | 1 | //! Special handling for AVM2 orphan objects | COMMENT |
| LOW | core/src/orphan_manager.rs | 61 | COMMENT | |
| LOW | core/src/tag_utils.rs | 41 | Continue, | COMMENT |
| LOW | core/src/config.rs | 61 | /// All networking APIs are permitted in the SWF file. | COMMENT |
| LOW | core/src/streams.rs | 61 | fn from(err: SubstreamError) -> NetstreamError { | COMMENT |
| LOW | core/src/streams.rs | 141 | Avm2(NetStreamObject<'gc>), | COMMENT |
| LOW | core/src/streams.rs | 221 | /// This points to the first byte that the stream has *never* processed | COMMENT |
| LOW | core/src/streams.rs | 461 | /// either the prior or next keyframe depending on seek direction. If the | COMMENT |
| LOW | core/src/streams.rs | 801 | /// Generally speaking, streams are only to be used once. However, the | COMMENT |
| LOW | core/src/limits.rs | 1 | use crate::context::UpdateContext; | COMMENT |
| LOW | core/src/display_object.rs | 81 | /// - Changing the color transform | COMMENT |
| LOW | core/src/display_object.rs | 201 | /// Masks are usually skipped when rendering, but when e.g. rendering | COMMENT |
| LOW | core/src/display_object.rs | 241 | /// Stencil masks are the classic, default masks used in Flash Player. | COMMENT |
| LOW | core/src/display_object.rs | 301 | /// The opaque background color of this display object. | COMMENT |
| LOW | core/src/display_object.rs | 481 | COMMENT | |
| LOW | core/src/display_object.rs | 1241 | // There are two parts to 'DisplayObject.scrollRect': | COMMENT |
| 1403 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …_gnash/actionscript.all/TextField-v7/output.fp9-14.txt | 546 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …_gnash/actionscript.all/TextField-v7/output.fp9-14.txt | 548 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …gnash/actionscript.all/TextField-v7/output.fp13-17.txt | 546 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …gnash/actionscript.all/TextField-v7/output.fp13-17.txt | 548 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …om_gnash/actionscript.all/TextField-v7/output.fp16.txt | 546 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …om_gnash/actionscript.all/TextField-v7/output.fp16.txt | 548 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …actionscript.all/TextField-v7/output.fp9-14.ruffle.txt | 546 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …actionscript.all/TextField-v7/output.fp9-14.ruffle.txt | 548 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …h/actionscript.all/TextField-v7/output.fp16.ruffle.txt | 546 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …h/actionscript.all/TextField-v7/output.fp16.ruffle.txt | 548 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …ctionscript.all/TextField-v7/output.fp13-17.ruffle.txt | 546 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …ctionscript.all/TextField-v7/output.fp13-17.ruffle.txt | 548 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …_gnash/actionscript.all/TextField-v6/output.fp9-14.txt | 521 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …_gnash/actionscript.all/TextField-v6/output.fp9-14.txt | 523 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …gnash/actionscript.all/TextField-v6/output.fp13-17.txt | 521 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …gnash/actionscript.all/TextField-v6/output.fp13-17.txt | 523 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …om_gnash/actionscript.all/TextField-v6/output.fp16.txt | 521 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …om_gnash/actionscript.all/TextField-v6/output.fp16.txt | 523 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …actionscript.all/TextField-v6/output.fp9-14.ruffle.txt | 521 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …actionscript.all/TextField-v6/output.fp9-14.ruffle.txt | 523 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …h/actionscript.all/TextField-v6/output.fp16.ruffle.txt | 521 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …h/actionscript.all/TextField-v6/output.fp16.ruffle.txt | 523 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …ctionscript.all/TextField-v6/output.fp13-17.ruffle.txt | 521 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …ctionscript.all/TextField-v6/output.fp13-17.ruffle.txt | 523 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …_gnash/actionscript.all/TextField-v8/output.fp9-14.txt | 547 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …_gnash/actionscript.all/TextField-v8/output.fp9-14.txt | 549 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …gnash/actionscript.all/TextField-v8/output.fp13-17.txt | 547 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …gnash/actionscript.all/TextField-v8/output.fp13-17.txt | 549 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …om_gnash/actionscript.all/TextField-v8/output.fp16.txt | 547 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …om_gnash/actionscript.all/TextField-v8/output.fp16.txt | 549 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …actionscript.all/TextField-v8/output.fp9-14.ruffle.txt | 547 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …actionscript.all/TextField-v8/output.fp9-14.ruffle.txt | 549 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …h/actionscript.all/TextField-v8/output.fp16.ruffle.txt | 547 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …h/actionscript.all/TextField-v8/output.fp16.ruffle.txt | 549 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …ctionscript.all/TextField-v8/output.fp13-17.ruffle.txt | 547 | PASSED: tf._target == '/fake_name' [./TextField.as:1207] | CODE |
| LOW | …ctionscript.all/TextField-v8/output.fp13-17.ruffle.txt | 549 | PASSED: tf._name == 'fake_name' [./TextField.as:1209] | CODE |
| LOW | …sts/tests/swfs/avm2/edittext_getlinemetrics/output.txt | 16 | //Lorem ipsum dolor sit amet!! | COMMENT |
| LOW | …sts/tests/swfs/avm2/edittext_getlinemetrics/output.txt | 16 | //Lorem ipsum dolor sit amet!! | COMMENT |
| LOW | …sts/tests/swfs/avm2/edittext_getlinemetrics/output.txt | 97 | //Lorem ipsum\n\ndolor sit\n amet | COMMENT |
| LOW | tests/tests/swfs/avm1/gettextextent/output.txt | 14 | /// new TextFormat("Noto Sans").getTextExtent("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); | COMMENT |
| LOW | tests/tests/swfs/avm1/gettextextent/output.txt | 14 | /// new TextFormat("Noto Sans").getTextExtent("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); | COMMENT |
| LOW | tests/tests/swfs/avm1/gettextextent/output.txt | 27 | /// new TextFormat("Noto Sans").getTextExtent("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 100); | COMMENT |
| LOW | tests/tests/swfs/avm1/gettextextent/output.txt | 27 | /// new TextFormat("Noto Sans").getTextExtent("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 100); | COMMENT |
| LOW | tests/tests/swfs/avm1/gettextextent/output.txt | 40 | /// new TextFormat("Noto Sans", 18).getTextExtent("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 100); | COMMENT |
| LOW | tests/tests/swfs/avm1/gettextextent/output.txt | 40 | /// new TextFormat("Noto Sans", 18).getTextExtent("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 100); | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | deny.toml | 66 | # need it (Flash Player didn't support it), so it would just add bloat. | COMMENT |
| MEDIUM | core/macros/src/lib.rs | 25 | /// TODO: This isn't completely robust for all cases, but should be good enough | COMMENT |
| MEDIUM | core/src/avm2/activation.rs | 93 | /// NOTE: In avmplus this is more nuanced — the default namespace is a property | COMMENT |
| MEDIUM | core/src/avm1/object_reference.rs | 143 | // Essentially, changes to `_name` are reverted after the clip is re-created | COMMENT |
| MEDIUM | core/src/avm1/globals/movie_clip.rs | 1031 | // Definitely not Object properties. | COMMENT |
| MEDIUM | tests/framework/src/backends/ui.rs | 77 | /// Fundamentally, this is mostly the same as [`NullUiBackend`] with the following differences: | COMMENT |
| MEDIUM | tests/tests/regression_tests.rs | 89 | // Manual tests here, since #[test] doesn't work once we use our own test harness | COMMENT |
| MEDIUM | render/src/shape_utils.rs | 1033 | // 1) Solving the quadratic needs to be numerically robust, particularly near the endpoints 0.0 and 1.0, and as the | COMMENT |
| MEDIUM | render/src/shape_utils.rs | 1169 | /// TODO: This probably isn't numerically robust | COMMENT |
| LOW | .github/workflows/test_rust.yml | 200 | # If not, someone force pushed and just use the parent | COMMENT |
| LOW | .github/workflows/download_translations.yml | 32 | # so just create them here instead of relying on Crowdin. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | web/packages/selfhosted/test/utils.ts | 243 | export async function hideHardwareAccelerationModal( | CODE |
| LOW | …selfhosted/test/integration_tests/context_menu/test.ts | 17 | async function supportsClipboardReadText(): Promise<boolean> { | CODE |
| LOW | web/packages/core/src/js-polyfills.ts | 16 | function polyfillArrayPrototypeReduce() { | CODE |
| LOW | web/packages/core/src/ruffle-imports.ts | 13 | export function copyToAudioBufferInterleaved( | CODE |
| LOW | web/packages/core/src/internal/player/inner.tsx | 2440 | export function workaroundYoutubeMixedContent( | CODE |
| LOW | web/packages/extension/src/utils.ts | 15 | export function enableBrowserOnOutdatedChromium() { | CODE |
| LOW | web/packages/extension/src/utils.ts | 83 | export async function hasHostPermissionForSpecifiedTab( | CODE |
| LOW | web/packages/extension/src/utils.ts | 98 | export async function hasHostPermissionForActiveTab() { | CODE |
| LOW | web/packages/extension/src/background.ts | 13 | async function getPluginPolyfillRegistration( | CODE |
| LOW | web/packages/extension/src/background.ts | 39 | async function isHeaderConditionSupported() { | CODE |
| LOW | .github/scripts/release.py | 46 | def get_today_nightly_tag_name(): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | …/swfs/from_gnash/misc-mtasc.all/inheritance/output.txt | 10 | PASSED: this.constructor.prototype.constructor.prototype.test() [inheritance.as:34] | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | render/src/lines.rs | 46 | // Step 1: Create a 1px thick line with the proper length. | COMMENT |
| LOW⚡ | render/src/lines.rs | 48 | // Step 2: Rotate it, so it still starts at `(0,0)` but points to `b-a`. | COMMENT |
| LOW⚡ | render/src/lines.rs | 50 | // Step 3: Translate it to `a`, so that it points to `b`. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | render/src/lines.rs | 46 | // Step 1: Create a 1px thick line with the proper length. | COMMENT |
| LOW⚡ | render/src/lines.rs | 48 | // Step 2: Rotate it, so it still starts at `(0,0)` but points to `b-a`. | COMMENT |
| LOW⚡ | render/src/lines.rs | 50 | // Step 3: Translate it to `a`, so that it points to `b`. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | …tests/swfs/avm2/focus_events_mouse_basic/input.json.py | 15 | 'void', 'esc', # ================ | CODE |
| MEDIUM | …tests/swfs/avm2/focus_events_mouse_basic/input.json.py | 29 | 'void', 'esc', # ================ | CODE |
| MEDIUM | …tests/swfs/avm2/focus_events_mouse_basic/input.json.py | 37 | 'void', 'esc', # ================ | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .github/scripts/release.py | 271 | CODE | |
| LOW | .github/scripts/coverage.py | 94 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .github/scripts/coverage.py | 6 | CODE | |
| LOW | .github/scripts/coverage.py | 7 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | core/src/avm1/property_decl.rs | 286 | /// # Usage: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | web/packages/extension/tools/submit_xpi.ts | 107 | # OR alternatively, if you have to use 'sudo docker', make sure to use $(sudo docker ...) for the second docker command: | COMMENT |