Open-source SEO, GEO, and marketing skills for AI agents.
This report summarizes scanner evidence for nowork-studio/NotFair, a Python project with 3,917 GitHub stars. SynthScan examined 32,386 lines of code across 277 source files, recording 430 pattern matches distributed across 13 syntactic categories. The legacy adjusted pattern signal is 23.6 (Elevated 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.
Can maintainers explain intent, assumptions, and trade-offs? 260 finding(s).
Are imports declared and package/API names verified? 15 finding(s).
Are wrappers, guards, and repeated structures necessary? 371 finding(s).
Which changes need focused human review? 430 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 430 distinct pattern matches across 13 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 |
|---|---|---|---|---|
| MEDIUM | test/install.test.sh | 15 | # ─── Helpers ───────────────────────────────────────────────── | COMMENT |
| MEDIUM | test/install.test.sh | 136 | # ─── Test 1: Plugin metadata exists and is valid ───────────── | COMMENT |
| MEDIUM | test/install.test.sh | 186 | # ─── Test 2: All skills exist with SKILL.md ────────────────── | COMMENT |
| MEDIUM | test/install.test.sh | 206 | # ─── Test 3: Legacy structure removed, helper bin retained ───────── | COMMENT |
| MEDIUM | test/install.test.sh | 251 | # ─── Test 4: Shared preambles exist ───────────────────────── | COMMENT |
| MEDIUM | test/install.test.sh | 278 | # ─── Test 5: MCP server configuration ─────────────────────── | COMMENT |
| MEDIUM | test/install.test.sh | 304 | # ─── Test 6: Connectors section in README ───────────────────── | COMMENT |
| MEDIUM | test/install.test.sh | 319 | # ─── Test 7: Reference docs exist ─────────────────────────── | COMMENT |
| MEDIUM | test/install.test.sh | 329 | # ─── Test 8: Eval fixtures exist where shipped ─────────────── | COMMENT |
| MEDIUM | test/install.test.sh | 340 | # ─── Test 9: Frontmatter has argument-hint ─────────────────── | COMMENT |
| MEDIUM | test/install.test.sh | 352 | # ─── Results ────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | test/unit/test_strapi_scripts.py | 353 | # ── preflight_strapi.py — SSRF protection ───────────────────────────────────── | COMMENT |
| MEDIUM | test/unit/test_strapi_scripts.py | 17 | # ── Load scripts without executing main() ──────────────────────────────────── | COMMENT |
| MEDIUM | test/unit/test_strapi_scripts.py | 41 | # ── fetch_strapi_content.py ─────────────────────────────────────────────────── | COMMENT |
| MEDIUM | test/unit/test_strapi_scripts.py | 250 | # ── push_strapi_seo.py ──────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | test/unit/test_cms_scripts.py | 94 | # ── fetch_wordpress_content.py ──────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | test/unit/test_cms_scripts.py | 265 | # ── fetch_contentful_content.py ─────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | test/unit/test_cms_scripts.py | 380 | # ── fetch_ghost_content.py ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | test/unit/test_cms_scripts.py | 458 | # ── SSRF protection (shared logic, tested via WordPress) ────────────────────── | COMMENT |
| MEDIUM⚡ | test/unit/test_cms_scripts.py | 482 | # ── WordPress auth header ───────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | test/unit/test_cms_scripts.py | 504 | # ── SEO audit (Ghost, same logic as WordPress) ──────────────────────────────── | COMMENT |
| MEDIUM | test/unit/test_cms_scripts.py | 16 | # ── Load scripts without executing main() ──────────────────────────────────── | COMMENT |
| MEDIUM | test/unit/test_cms_scripts.py | 39 | # ── cms_detect.py ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/notfair-update-check.sh | 28 | # ─── Dev symlink: skip update checks entirely ──────────────── | COMMENT |
| MEDIUM | scripts/notfair-update-check.sh | 42 | # ─── Step 0: Check if updates are disabled ──────────────────── | COMMENT |
| MEDIUM | scripts/notfair-update-check.sh | 48 | # ─── Snooze helper ────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/notfair-update-check.sh | 91 | # ─── Step 1: Read and validate local version ───────────────── | COMMENT |
| MEDIUM | scripts/notfair-update-check.sh | 104 | # ─── Step 2: Check "just upgraded" marker ───────────────────── | COMMENT |
| MEDIUM | scripts/notfair-update-check.sh | 117 | # ─── Step 3: Check cache freshness ────────────────────────── | COMMENT |
| MEDIUM | scripts/notfair-update-check.sh | 151 | # ─── Step 4: Slow path — fetch remote version ──────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/push_strapi_seo.py | 62 | # ── SSRF protection ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/push_strapi_seo.py | 102 | # ── Config loading ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/push_strapi_seo.py | 151 | # ── HTTP helpers with retry ─────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | seo/seo-analysis/scripts/push_strapi_seo.py | 208 | # ── Version detection ───────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | seo/seo-analysis/scripts/push_strapi_seo.py | 218 | # ── Current SEO value fetching ──────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/push_strapi_seo.py | 245 | # ── Payload builder ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/push_strapi_seo.py | 278 | # ── Diff display ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/push_strapi_seo.py | 299 | # ── Confirmation ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/push_strapi_seo.py | 311 | # ── Core update logic ───────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/push_strapi_seo.py | 416 | # ── CLI ─────────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_wordpress.py | 33 | # ── Config loading ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_wordpress.py | 81 | # ── Security: SSRF protection ───────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_wordpress.py | 125 | # ── HTTP helper ─────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_wordpress.py | 171 | # ── Validation checks ───────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_contentful.py | 29 | # ── Config loading ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_contentful.py | 78 | # ── HTTP helper with retry ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_contentful.py | 113 | # ── Validation checks ───────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/fetch_ghost_content.py | 46 | # ── Config loading ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/fetch_ghost_content.py | 130 | # ── HTTP helper with retry ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/fetch_ghost_content.py | 197 | # ── Response normalisation ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/fetch_ghost_content.py | 239 | # ── Pagination ──────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/fetch_ghost_content.py | 281 | # ── SEO audit ───────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_ghost.py | 32 | # ── Config loading ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_ghost.py | 79 | # ── Security: SSRF protection ───────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_ghost.py | 122 | # ── HTTP helper with retry ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/preflight_ghost.py | 153 | # ── Validation checks ───────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/fetch_strapi_content.py | 43 | # ── SSRF protection ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/fetch_strapi_content.py | 83 | # ── Config loading ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/fetch_strapi_content.py | 132 | # ── HTTP helper with retry ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM | seo/seo-analysis/scripts/fetch_strapi_content.py | 167 | # ── Version detection ───────────────────────────────────────────────────────── | COMMENT |
| 22 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | test/test_skill_routing_e2e.py | 150 | def test_routing_seo_triggers(): | CODE |
| LOW | test/test_skill_routing_e2e.py | 164 | def test_routing_seo_non_triggers(): | CODE |
| LOW | test/test_skill_llm_eval.py | 59 | def test_seo_quick_wins_clarity(): | CODE |
| LOW | test/test_skill_llm_eval.py | 67 | def test_seo_report_format_clarity(): | CODE |
| LOW⚡ | test/test_ads_skill_llm_eval.py | 55 | def test_ads_heuristics_clarity(): | CODE |
| LOW⚡ | test/test_ads_skill_llm_eval.py | 63 | def test_ads_workflows_clarity(): | CODE |
| LOW⚡ | test/test_ads_skill_llm_eval.py | 71 | def test_ads_audit_scoring_clarity(): | CODE |
| LOW⚡ | test/test_ads_skill_llm_eval.py | 79 | def test_ads_audit_report_clarity(): | CODE |
| LOW⚡ | test/test_ads_skill_llm_eval.py | 87 | def test_ads_copy_formulas_clarity(): | CODE |
| LOW | test/test_mcp_skill_e2e.py | 36 | def test_universal_mcp_is_the_only_registered_server_and_skills_are_portable(): | CODE |
| LOW | test/test_mcp_skill_e2e.py | 71 | def test_ads_wrappers_resolve_from_the_plugin_root_without_fallback(): | CODE |
| LOW | test/test_mcp_skill_e2e.py | 82 | def test_all_host_configs_and_registry_use_one_versioned_connection(): | CODE |
| LOW | test/test_mcp_skill_e2e.py | 116 | def test_active_plugin_files_do_not_advertise_legacy_endpoints(): | CODE |
| LOW | test/test_mcp_skill_e2e.py | 140 | def test_live_mcp_oauth_discovery_round_trip(origin): | CODE |
| LOW | test/test_skill_e2e.py | 76 | def test_seo_no_gsc_technical_audit(): | CODE |
| LOW | test/test_skill_e2e.py | 118 | def test_seo_fixture_full_audit(): | CODE |
| LOW | test/unit/test_security_boundaries.py | 34 | def test_private_aliases_and_nonpublic_addresses_never_connect(self): | CODE |
| LOW | test/unit/test_security_boundaries.py | 44 | def test_all_dns_answers_validated_and_actual_connect_pinned(self): | CODE |
| LOW | test/unit/test_security_boundaries.py | 55 | def test_scheme_credentials_and_proxy_cannot_bypass(self): | CODE |
| LOW | test/unit/test_security_boundaries.py | 65 | def test_redirects_recheck_destination_and_preserve_public_fetch(self): | CODE |
| LOW | test/unit/test_security_boundaries.py | 93 | def test_every_cms_get_preserves_same_origin_and_rejects_other_origin(self): | CODE |
| LOW | test/unit/test_security_boundaries.py | 124 | def test_origin_normalization_scheme_changes_and_put_behavior(self): | CODE |
| LOW | test/unit/test_security_boundaries.py | 134 | def test_both_metadata_fields_are_text_for_every_json_type(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 161 | def test_meta_description_too_short(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 167 | def test_meta_description_too_long(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 173 | def test_root_field_fallback_for_meta_title(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 180 | def test_seo_schema_component_when_seo_key_present(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 186 | def test_all_fields_present_with_valid_seo(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 275 | def test_component_schema_wraps_in_seo(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 281 | def test_root_fields_schema_flat(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 287 | def test_returns_none_when_nothing_to_update(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 291 | def test_merges_existing_seo_component(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 299 | def test_both_fields_in_update(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 306 | def test_auto_detect_component_from_current_attrs(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 312 | def test_auto_detect_root_when_no_seo_key(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 319 | def test_none_current_attrs_handled(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 328 | def test_guard_skips_when_timestamps_match(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 333 | def test_guard_fires_when_timestamps_differ(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 339 | def test_guard_skips_check_when_expected_empty(self): | CODE |
| LOW⚡ | test/unit/test_strapi_scripts.py | 346 | def test_guard_skips_check_when_live_empty(self): | CODE |
| LOW | test/unit/test_strapi_scripts.py | 74 | def test_returns_empty_dict_for_none(self): | CODE |
| LOW | test/unit/test_strapi_scripts.py | 78 | def test_returns_empty_dict_for_empty_dict(self): | CODE |
| LOW | test/unit/test_strapi_scripts.py | 94 | def test_missing_fields_default_to_empty_string(self): | CODE |
| LOW | test/unit/test_strapi_scripts.py | 138 | def test_title_fallback_to_name(self): | CODE |
| LOW | test/unit/test_strapi_scripts.py | 145 | def test_missing_meta_title_flag(self): | CODE |
| LOW | test/unit/test_strapi_scripts.py | 233 | def test_entry_with_multiple_issues_counted_once_in_complete(self): | CODE |
| LOW | test/unit/test_strapi_scripts.py | 376 | def test_public_ip_is_not_private(self): | CODE |
| LOW | test/unit/test_strapi_scripts.py | 382 | def test_invalid_string_returns_false(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 89 | def test_missing_file_returns_empty(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 102 | def test_yoast_head_json_extracted(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 128 | def test_yoast_takes_priority_over_rank_math(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 136 | def test_no_seo_plugin_returns_empty_meta_title(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 146 | def test_partial_yoast_missing_description(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 173 | def test_document_id_is_string(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 182 | def test_seo_fields_extracted(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 187 | def test_missing_meta_title_flagged(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 195 | def test_meta_title_too_long_flagged(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 207 | def test_meta_description_too_short_flagged(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 213 | def test_meta_description_too_long_flagged(self): | CODE |
| LOW⚡ | test/unit/test_cms_scripts.py | 219 | def test_meta_description_in_range_not_flagged(self): | CODE |
| 156 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | test/test_skill_e2e.py | 141 | except Exception: | CODE |
| HIGH | seo/seo-analysis/scripts/push_strapi_seo.py | 98 | except (socket.gaierror, OSError): | CODE |
| HIGH | seo/seo-analysis/scripts/push_strapi_seo.py | 117 | except (OSError, PermissionError): | CODE |
| HIGH | seo/seo-analysis/scripts/url_inspection.py | 49 | except (OSError, ValueError): | CODE |
| HIGH | seo/seo-analysis/scripts/url_inspection.py | 234 | except (ValueError, TypeError): | CODE |
| HIGH | seo/seo-analysis/scripts/_uid.py | 76 | except (OSError, NotImplementedError): | CODE |
| HIGH | seo/seo-analysis/scripts/_uid.py | 85 | except OSError: | CODE |
| HIGH | seo/seo-analysis/scripts/preflight_wordpress.py | 48 | except (OSError, PermissionError): | CODE |
| HIGH | seo/seo-analysis/scripts/preflight_wordpress.py | 121 | except (socket.gaierror, OSError): | CODE |
| HIGH | seo/seo-analysis/scripts/preflight_wordpress.py | 246 | except Exception: | CODE |
| HIGH | seo/seo-analysis/scripts/analyze_gsc.py | 51 | except (OSError, ValueError): | CODE |
| HIGH | seo/seo-analysis/scripts/preflight_contentful.py | 44 | except (OSError, PermissionError): | CODE |
| HIGH | seo/seo-analysis/scripts/pagespeed.py | 41 | except OSError: | CODE |
| HIGH | seo/seo-analysis/scripts/fetch_ghost_content.py | 61 | except (OSError, PermissionError): | CODE |
| HIGH | seo/seo-analysis/scripts/fetch_ghost_content.py | 126 | except (socket.gaierror, OSError): | CODE |
| HIGH | seo/seo-analysis/scripts/preflight_ghost.py | 47 | except (OSError, PermissionError): | CODE |
| HIGH | seo/seo-analysis/scripts/preflight_ghost.py | 118 | except (socket.gaierror, OSError): | CODE |
| HIGH | seo/seo-analysis/scripts/fetch_strapi_content.py | 79 | except (socket.gaierror, OSError): | CODE |
| HIGH | seo/seo-analysis/scripts/fetch_strapi_content.py | 98 | except (OSError, PermissionError): | CODE |
| HIGH | seo/seo-analysis/scripts/preflight_strapi.py | 43 | except (OSError, PermissionError): | CODE |
| HIGH | seo/seo-analysis/scripts/preflight_strapi.py | 95 | except (socket.gaierror, OSError): | CODE |
| HIGH | seo/seo-analysis/scripts/fetch_wordpress_content.py | 76 | except (socket.gaierror, OSError): | CODE |
| HIGH | seo/seo-analysis/scripts/fetch_wordpress_content.py | 95 | except (OSError, PermissionError): | CODE |
| HIGH | seo/seo-analysis/scripts/cms_detect.py | 31 | except (OSError, PermissionError): | CODE |
| HIGH | seo/seo-analysis/scripts/list_gsc_sites.py | 25 | except (OSError, ValueError): | CODE |
| HIGH | seo/seo-analysis/scripts/fetch_contentful_content.py | 54 | except (OSError, PermissionError): | CODE |
| HIGH | seo/seo-analysis/scripts/preflight.py | 51 | except FileNotFoundError: | CODE |
| HIGH | seo/seo-analysis/scripts/preflight.py | 377 | except OSError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | test/test_skill_e2e.py | 141 | except Exception: | CODE |
| LOW | test/helpers/eval_store.py | 57 | except Exception: | CODE |
| LOW | test/helpers/eval_store.py | 65 | except Exception: | CODE |
| MEDIUM | test/helpers/eval_store.py | 46 | def _git_info() -> tuple[str, str]: | CODE |
| LOW | test/helpers/llm_judge.py | 62 | except Exception as e: | CODE |
| LOW | scripts/notfair-change-watch.sh | 114 | except Exception: | STRING |
| LOW | seo/seo-analysis/scripts/push_strapi_seo.py | 76 | except Exception: | CODE |
| LOW⚡ | seo/seo-analysis/scripts/push_strapi_seo.py | 226 | except Exception as e: | CODE |
| LOW | seo/seo-analysis/scripts/push_strapi_seo.py | 331 | except Exception: | CODE |
| MEDIUM | seo/seo-analysis/scripts/push_strapi_seo.py | 64 | def _is_private_ip(ip_str): | CODE |
| LOW | seo/seo-analysis/scripts/url_inspection.py | 319 | except Exception as exc: | CODE |
| LOW | seo/seo-analysis/scripts/_uid.py | 50 | except Exception: | CODE |
| LOW | seo/seo-analysis/scripts/_uid.py | 82 | except Exception: | CODE |
| LOW | seo/seo-analysis/scripts/preflight_wordpress.py | 95 | except Exception: | CODE |
| LOW | seo/seo-analysis/scripts/preflight_wordpress.py | 246 | except Exception: | CODE |
| MEDIUM | seo/seo-analysis/scripts/preflight_wordpress.py | 83 | def _is_private_ip(ip_str): | CODE |
| LOW | seo/seo-analysis/scripts/analyze_gsc.py | 539 | except Exception: | CODE |
| LOW | seo/seo-analysis/scripts/analyze_gsc.py | 618 | except Exception as exc: | CODE |
| MEDIUM | seo/seo-analysis/scripts/analyze_gsc.py | 56 | def get_access_token(): | CODE |
| LOW | seo/seo-analysis/scripts/pagespeed.py | 74 | except Exception as e: | CODE |
| LOW | seo/seo-analysis/scripts/pagespeed.py | 305 | except Exception as exc: | CODE |
| LOW | seo/seo-analysis/scripts/fetch_ghost_content.py | 105 | except Exception: | CODE |
| MEDIUM | seo/seo-analysis/scripts/fetch_ghost_content.py | 94 | def _is_private_ip(ip_str): | CODE |
| LOW | seo/seo-analysis/scripts/preflight_ghost.py | 92 | except Exception: | CODE |
| MEDIUM | seo/seo-analysis/scripts/preflight_ghost.py | 81 | def _is_private_ip(ip_str): | CODE |
| LOW | seo/seo-analysis/scripts/fetch_strapi_content.py | 57 | except Exception: | CODE |
| MEDIUM | seo/seo-analysis/scripts/fetch_strapi_content.py | 45 | def _is_private_ip(ip_str): | CODE |
| LOW | seo/seo-analysis/scripts/preflight_strapi.py | 104 | except Exception: | CODE |
| LOW | seo/seo-analysis/scripts/preflight_strapi.py | 252 | except Exception: | CODE |
| LOW | seo/seo-analysis/scripts/fetch_wordpress_content.py | 55 | except Exception: | CODE |
| MEDIUM | seo/seo-analysis/scripts/fetch_wordpress_content.py | 44 | def _is_private_ip(ip_str): | CODE |
| LOW | seo/seo-analysis/scripts/preflight.py | 189 | except Exception: | CODE |
| LOW | seo/broken-link-checker/scripts/checker.py | 135 | except Exception as e2: | CODE |
| LOW | seo/broken-link-checker/scripts/checker.py | 140 | except Exception as e: | CODE |
| LOW | seo/broken-link-checker/scripts/checker.py | 160 | except Exception as e: | CODE |
| LOW | seo/broken-link-checker/scripts/checker.py | 210 | except Exception as e: | CODE |
| LOW | seo/broken-link-checker/scripts/checker.py | 225 | except Exception as e: | CODE |
| MEDIUM | seo/broken-link-checker/scripts/checker.py | 226 | print(f"Error crawling {current_url}: {e}", file=sys.stderr) | CODE |
| MEDIUM | seo/broken-link-checker/scripts/checker.py | 26 | def _validate_public_url(url): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | seo/seo-analysis/scripts/url_inspection.py | 0 | return the quota_project_id from the adc json file, or none. | STRING |
| HIGH | seo/seo-analysis/scripts/analyze_gsc.py | 0 | return the quota_project_id from the adc json file, or none. | STRING |
| HIGH | seo/seo-analysis/scripts/list_gsc_sites.py | 0 | return the quota_project_id from the adc json file, or none. | STRING |
| HIGH | seo/seo-analysis/scripts/fetch_ghost_content.py | 0 | paginate through all entries. returns list of normalised entries. | STRING |
| HIGH | seo/seo-analysis/scripts/fetch_wordpress_content.py | 0 | paginate through all entries. returns list of normalised entries. | STRING |
| HIGH | seo/seo-analysis/scripts/fetch_contentful_content.py | 0 | paginate through all entries. returns list of normalised entries. | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | test/unit/test_security_boundaries.py | 65 | CODE | |
| LOW | test/unit/test_security_boundaries.py | 93 | CODE | |
| LOW | test/unit/test_broken_link_checker.py | 24 | CODE | |
| LOW | test/unit/test_broken_link_checker.py | 39 | CODE | |
| LOW | test/helpers/session_runner.py | 49 | CODE | |
| LOW | seo/seo-analysis/scripts/push_strapi_seo.py | 104 | CODE | |
| LOW | seo/seo-analysis/scripts/push_strapi_seo.py | 418 | CODE | |
| LOW | seo/seo-analysis/scripts/url_inspection.py | 212 | CODE | |
| LOW | seo/seo-analysis/scripts/url_inspection.py | 251 | CODE | |
| LOW | seo/seo-analysis/scripts/preflight_wordpress.py | 35 | CODE | |
| LOW | seo/seo-analysis/scripts/preflight_wordpress.py | 199 | CODE | |
| LOW | seo/seo-analysis/scripts/analyze_gsc.py | 151 | CODE | |
| LOW | seo/seo-analysis/scripts/show_pagespeed.py | 78 | CODE | |
| LOW | seo/seo-analysis/scripts/preflight_contentful.py | 31 | CODE | |
| LOW | seo/seo-analysis/scripts/pagespeed.py | 29 | CODE | |
| LOW | seo/seo-analysis/scripts/fetch_ghost_content.py | 48 | CODE | |
| LOW | seo/seo-analysis/scripts/preflight_ghost.py | 34 | CODE | |
| LOW | seo/seo-analysis/scripts/fetch_strapi_content.py | 85 | CODE | |
| LOW | seo/seo-analysis/scripts/preflight_strapi.py | 30 | CODE | |
| LOW | seo/seo-analysis/scripts/fetch_wordpress_content.py | 82 | CODE | |
| LOW | seo/seo-analysis/scripts/cms_detect.py | 18 | CODE | |
| LOW | seo/seo-analysis/scripts/fetch_contentful_content.py | 41 | CODE | |
| LOW | seo/seo-analysis/scripts/preflight.py | 31 | CODE | |
| LOW | seo/seo-analysis/scripts/preflight.py | 357 | CODE | |
| LOW | seo/broken-link-checker/scripts/checker.py | 116 | CODE | |
| LOW | seo/broken-link-checker/scripts/checker.py | 144 | CODE | |
| LOW | seo/broken-link-checker/scripts/checker.py | 105 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | google-ads/audit/references/business-context.md | 7 | ### Step 1: Resolve the website URL | COMMENT |
| LOW | google-ads/audit/references/business-context.md | 13 | ### Step 2: Crawl the website | COMMENT |
| LOW | google-ads/audit/references/business-context.md | 32 | ### Step 3: Extract business context from crawled pages | COMMENT |
| LOW | seo/seo-analysis/WORKFLOW.md | 721 | ### Step 1: Find Queries Without Dedicated Pages | COMMENT |
| LOW | seo/seo-analysis/WORKFLOW.md | 742 | ### Step 2: Build Topic Clusters from GSC Data | COMMENT |
| LOW | seo/seo-analysis/WORKFLOW.md | 752 | ### Step 3: Business Context Gap Check | COMMENT |
| LOW | seo/seo-analysis/WORKFLOW.md | 763 | ### Step 4: Offer Deeper Keyword Research | COMMENT |
| LOW | …keyword-research/references/topic-cluster-templates.md | 13 | ### Step 1: Define the Pillar Topic | COMMENT |
| LOW | …keyword-research/references/topic-cluster-templates.md | 36 | ### Step 2: Map Cluster Subtopics | COMMENT |
| LOW | …keyword-research/references/topic-cluster-templates.md | 49 | ### Step 3: Define Internal Linking Map | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | test/unit/test_security_boundaries.py | 6 | CODE | |
| LOW | test/unit/test_security_boundaries.py | 14 | CODE | |
| LOW | test/unit/test_strapi_scripts.py | 12 | CODE | |
| LOW | test/helpers/touchfiles.py | 11 | CODE | |
| LOW | scripts/notfair-content-calendar.py | 16 | CODE | |
| LOW | seo/seo-analysis/scripts/url_inspection.py | 36 | CODE | |
| LOW | seo/seo-analysis/scripts/_uid.py | 28 | CODE | |
| LOW | seo/seo-analysis/scripts/analyze_gsc.py | 24 | CODE | |
| LOW | seo/seo-analysis/scripts/list_gsc_sites.py | 12 | CODE | |
| LOW | seo/broken-link-checker/scripts/checker.py | 15 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | test/unit/test_analyze_gsc.py | 364 | result = gsc.gsc_query('fake-token', 'sc-domain:test.com', {'dimensions': ['query']}) | CODE |
| LOW⚡ | test/unit/test_analyze_gsc.py | 370 | result = gsc.gsc_query('fake-token', 'sc-domain:test.com', {'dimensions': ['query']}) | CODE |
| LOW⚡ | test/unit/test_analyze_gsc.py | 392 | result = gsc.gsc_query('fake-token', 'sc-domain:test.com', {'dimensions': ['query']}) | CODE |
| LOW | test/unit/test_analyze_gsc.py | 248 | return gsc.pull_period_comparison('fake-token', 'sc-domain:test.com', 28) | CODE |
| LOW | seo/shared/business-context.md | 259 | "business_name": "Acme Corp", | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | test/install.test.sh | 4 | # Usage: | COMMENT |
| LOW | scripts/notfair-config.sh | 4 | # Usage: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | test/test_skill_routing_e2e.py | 103 | # Hard harness failure (timeout/crash) must not silently pass "should not trigger" tests | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/notfair-change-watch.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | scripts/notfair-change-watch.sh | 21 | # notfair-change-watch ics <account> <change_id> > reminder.ics | COMMENT |
| LOW | scripts/notfair-update-check.sh | 1 | #!/usr/bin/env bash | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | test/unit/test_broken_link_checker.py | 148 | # We need to check if /private is skipped. | COMMENT |