<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: CopperSunDev</title>
    <description>The latest articles on DEV Community by CopperSunDev (@coppersundev).</description>
    <link>https://dev.to/coppersundev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3659025%2F67b7af33-5040-4848-9b99-f2b9ccf2e6c3.png</url>
      <title>DEV Community: CopperSunDev</title>
      <link>https://dev.to/coppersundev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/coppersundev"/>
    <language>en</language>
    <item>
      <title>Is AI Transcription Safe? Security &amp; Privacy Guide</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Wed, 30 Sep 2026 17:57:36 +0000</pubDate>
      <link>https://dev.to/coppersundev/is-ai-transcription-safe-security-privacy-guide-587h</link>
      <guid>https://dev.to/coppersundev/is-ai-transcription-safe-security-privacy-guide-587h</guid>
      <description>&lt;p&gt;You're considering AI transcription for your organization. The productivity benefits are clear—automated meeting notes, searchable recordings, reduced documentation time. But your security team has questions you can't answer: Where does the data go? Who can access it? What happens if there's a breach?&lt;/p&gt;

&lt;p&gt;These concerns aren't theoretical. A &lt;a href="https://www.fisherphillips.com/en/news-insights/new-lawsuit-highlights-concerns-about-ai-notetakers.html" rel="noopener noreferrer"&gt;2024 lawsuit against Otter.ai&lt;/a&gt; alleges the service recorded conversations without proper consent and used that data to train its models. Whether the allegations hold up in court, they highlight the risks organizations face when adopting AI transcription without proper due diligence.&lt;/p&gt;

&lt;p&gt;This guide covers the real security and privacy considerations for AI transcription—not marketing claims, but the questions you need to answer before deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Navigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Real Risks of AI Transcription&lt;/li&gt;
&lt;li&gt;Data Retention: Where Your Recordings Go&lt;/li&gt;
&lt;li&gt;Consent Requirements You Can't Ignore&lt;/li&gt;
&lt;li&gt;Legal Discovery: The Hidden Liability&lt;/li&gt;
&lt;li&gt;Enterprise Security Checklist&lt;/li&gt;
&lt;li&gt;Vendor Evaluation Framework&lt;/li&gt;
&lt;li&gt;Industry-Specific Considerations&lt;/li&gt;
&lt;li&gt;BrassTranscripts Privacy Approach&lt;/li&gt;
&lt;li&gt;FAQ&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Real Risks of AI Transcription
&lt;/h2&gt;

&lt;p&gt;AI notetakers introduce several categories of risk that organizations must evaluate before deployment. According to &lt;a href="https://privacy.blog.fordham.edu/ai-notetakers-in-meetings-balancing-efficiency-with-privacy-and-risk/" rel="noopener noreferrer"&gt;Fordham University's Privacy Office&lt;/a&gt;, these risks span data security, regulatory compliance, consent, and AI training concerns:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Security Vulnerabilities&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud storage of recordings increases attack surface&lt;/li&gt;
&lt;li&gt;Third-party processing introduces additional breach vectors&lt;/li&gt;
&lt;li&gt;Metadata retention (even after content deletion) creates exposure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Regulatory Compliance Gaps&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FERPA implications for student information&lt;/li&gt;
&lt;li&gt;GDPR/CCPA requirements for personal data&lt;/li&gt;
&lt;li&gt;HIPAA restrictions on protected health information&lt;/li&gt;
&lt;li&gt;Industry-specific regulations (financial services, legal)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Consent and Legal Risks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recording without proper consent can violate wiretapping laws&lt;/li&gt;
&lt;li&gt;Attorney-client privilege may be compromised&lt;/li&gt;
&lt;li&gt;Transcripts become discoverable in litigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI Training Concerns&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some services use customer data to improve models&lt;/li&gt;
&lt;li&gt;"Deleted" content may persist in training datasets&lt;/li&gt;
&lt;li&gt;Model outputs could theoretically reproduce sensitive information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://www.jdsupra.com/legalnews/rise-in-popularity-of-ai-transcription-5003959/" rel="noopener noreferrer"&gt;Perkins Coie analysis of AI transcription litigation risks&lt;/a&gt; highlights that AI transcription records increase a business's logistical burdens and create written records that any attendee can save—expanding the universe of potentially discoverable materials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Retention: Where Your Recordings Go
&lt;/h2&gt;

&lt;p&gt;Not all AI transcription services handle data the same way. Understanding retention policies is essential for risk assessment.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Vendors Typically Store
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Audio/Video Files&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The original recording you upload or the meeting recording&lt;/li&gt;
&lt;li&gt;Storage duration varies from 24 hours to indefinite&lt;/li&gt;
&lt;li&gt;May be retained for quality assurance or model training&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Transcripts&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The text output of transcription&lt;/li&gt;
&lt;li&gt;Often stored longer than audio for user access&lt;/li&gt;
&lt;li&gt;May be searchable across your organization's history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Metadata&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Timestamps, participant names, meeting titles&lt;/li&gt;
&lt;li&gt;Integration data (CRM records, calendar info)&lt;/li&gt;
&lt;li&gt;Usage analytics and access logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Model Training Data&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some services explicitly use recordings to improve AI&lt;/li&gt;
&lt;li&gt;Others claim no training use but retain data&lt;/li&gt;
&lt;li&gt;Deletion doesn't guarantee removal from trained models&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Questions to Ask Vendors
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;How long do you retain audio recordings after transcription?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How long do you retain transcript text?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is our data used to train or improve your AI models?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What happens to our data if we cancel our subscription?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Can we request complete data deletion, and how is it verified?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Where is data physically stored (which countries/regions)?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Who at your company can access our recordings and transcripts?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Retention Policy Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Audio Retention&lt;/th&gt;
&lt;th&gt;Transcript Retention&lt;/th&gt;
&lt;th&gt;Training Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BrassTranscripts&lt;/td&gt;
&lt;td&gt;24 hours&lt;/td&gt;
&lt;td&gt;48 hours&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Otter.ai (Free)&lt;/td&gt;
&lt;td&gt;Indefinite&lt;/td&gt;
&lt;td&gt;Indefinite&lt;/td&gt;
&lt;td&gt;See ToS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Otter.ai (Enterprise)&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;No (with BAA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fireflies.ai (Free)&lt;/td&gt;
&lt;td&gt;Account lifetime&lt;/td&gt;
&lt;td&gt;Account lifetime&lt;/td&gt;
&lt;td&gt;See ToS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fireflies.ai (Enterprise)&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note: Policies change. Always verify current terms before deployment.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Consent Requirements You Can't Ignore
&lt;/h2&gt;

&lt;p&gt;Recording and transcribing conversations creates legal obligations that vary by jurisdiction and context.&lt;/p&gt;

&lt;h3&gt;
  
  
  One-Party vs All-Party Consent
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;One-Party Consent States/Countries&lt;/strong&gt;: One participant can record without informing others. However, this doesn't mean you &lt;em&gt;should&lt;/em&gt;—professional and ethical standards typically require disclosure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All-Party Consent Jurisdictions&lt;/strong&gt;: All participants must consent to recording. Violating this can result in civil and criminal liability.&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://insights.michaelbest.com/post/102kpai/legal-and-practical-considerations-for-ai-driven-meeting-transcription" rel="noopener noreferrer"&gt;Michael Best's legal analysis&lt;/a&gt;, organizations should:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Require explicit consent from all meeting participants before enabling transcription services... through either verbal consent at the beginning of the meeting or written consent beforehand as part of a broader acceptable-use policy."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Best Practices for Consent
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before Recording:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Announce that the meeting will be recorded and transcribed&lt;/li&gt;
&lt;li&gt;Explain how the recording will be used and stored&lt;/li&gt;
&lt;li&gt;Give participants the option to decline or leave&lt;/li&gt;
&lt;li&gt;Document consent (verbal acknowledgment is often recorded)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For External Participants:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Include recording disclosure in meeting invitations&lt;/li&gt;
&lt;li&gt;Use calendar invite descriptions to notify attendees&lt;/li&gt;
&lt;li&gt;Consider written consent for sensitive discussions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Employees:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Establish clear policies in employee handbook&lt;/li&gt;
&lt;li&gt;Provide training on consent requirements&lt;/li&gt;
&lt;li&gt;Create opt-out procedures for sensitive meetings&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Bot Announcement Problem
&lt;/h3&gt;

&lt;p&gt;AI meeting assistants like Fireflies and Otter send automated announcements when joining meetings: "This meeting is being recorded by [Service]." This creates a visible record that disclosure occurred, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Participants may not understand what "recorded" means&lt;/li&gt;
&lt;li&gt;Automated messages don't explain data handling&lt;/li&gt;
&lt;li&gt;Silent acceptance isn't the same as informed consent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider whether automated disclosure meets your organization's consent standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Legal Discovery: The Hidden Liability
&lt;/h2&gt;

&lt;p&gt;Once AI transcription generates text records of your meetings, those records become potentially discoverable in litigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Can Be Subpoenaed
&lt;/h3&gt;

&lt;p&gt;AI transcription creates multiple categories of potentially discoverable data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transcripts&lt;/strong&gt; of meetings discussing disputed matters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audio recordings&lt;/strong&gt; if retained by the service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata&lt;/strong&gt; showing who attended, when, and for how long&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search queries&lt;/strong&gt; run against transcript databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-generated summaries&lt;/strong&gt; and action items&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Litigation Hold Complications
&lt;/h3&gt;

&lt;p&gt;When litigation is reasonably anticipated, organizations must preserve relevant documents—including AI transcripts. This creates several challenges:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Over-Preservation&lt;/strong&gt;: Automatic transcription of all meetings generates massive amounts of potentially discoverable material that wouldn't exist without the technology.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deletion Risks&lt;/strong&gt;: If you delete transcripts after a litigation hold should have been in place, you risk sanctions for spoliation of evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vendor Dependencies&lt;/strong&gt;: Your ability to preserve data depends on the vendor's retention and export capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Risk Mitigation Strategies
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Selective Deployment&lt;/strong&gt;: Don't transcribe meetings where sensitive legal discussions occur&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retention Limits&lt;/strong&gt;: Choose services with short retention periods to minimize exposure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legal Review&lt;/strong&gt;: Consult counsel before implementing organization-wide transcription&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy Documentation&lt;/strong&gt;: Create clear policies about which meetings should (and shouldn't) be transcribed&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Enterprise Security Checklist
&lt;/h2&gt;

&lt;p&gt;Before deploying AI transcription, your security team should verify:&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure Security
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;SOC 2 Type II Certification&lt;/strong&gt;: Independent audit of security controls&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Encryption at Rest&lt;/strong&gt;: Data encrypted when stored&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Encryption in Transit&lt;/strong&gt;: TLS 1.2+ for all data transmission&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Geographic Data Residency&lt;/strong&gt;: Data stored in approved regions&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Penetration Testing&lt;/strong&gt;: Regular third-party security assessments&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Access Controls
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;SSO Integration&lt;/strong&gt;: SAML/OAuth for identity management&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Role-Based Access&lt;/strong&gt;: Granular permissions for transcript access&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Audit Logging&lt;/strong&gt;: Complete logs of who accessed what&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;MFA Support&lt;/strong&gt;: Multi-factor authentication available&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;IP Restrictions&lt;/strong&gt;: Ability to limit access by network&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Compliance Capabilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;BAA Available&lt;/strong&gt;: For HIPAA-covered entities&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;DPA Available&lt;/strong&gt;: For GDPR compliance&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Data Export&lt;/strong&gt;: Ability to extract all organizational data&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Data Deletion&lt;/strong&gt;: Verified deletion on request&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Retention Controls&lt;/strong&gt;: Configurable retention periods&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vendor Risk
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Financial Stability&lt;/strong&gt;: Vendor likely to remain operational&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Breach History&lt;/strong&gt;: Past security incidents and response&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Insurance Coverage&lt;/strong&gt;: Cyber liability insurance in place&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Subprocessor List&lt;/strong&gt;: Transparency about third parties&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Exit Strategy&lt;/strong&gt;: Data portability if changing vendors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Vendor Evaluation Framework
&lt;/h2&gt;

&lt;p&gt;Use this framework to compare AI transcription vendors on security criteria:&lt;/p&gt;

&lt;h3&gt;
  
  
  Tier 1: Must-Have (Eliminate vendors missing these)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Red Flag&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;Is data encrypted at rest and in transit?&lt;/td&gt;
&lt;td&gt;"We use standard security" (vague)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Location&lt;/td&gt;
&lt;td&gt;Where is data physically stored?&lt;/td&gt;
&lt;td&gt;"Various global locations" (unclear)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retention&lt;/td&gt;
&lt;td&gt;How long is data kept?&lt;/td&gt;
&lt;td&gt;"Until you delete it" (indefinite)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Training Use&lt;/td&gt;
&lt;td&gt;Is our data used for AI training?&lt;/td&gt;
&lt;td&gt;No clear answer in ToS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Tier 2: Should-Have (Weight heavily in evaluation)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Better Answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Compliance&lt;/td&gt;
&lt;td&gt;What certifications do you hold?&lt;/td&gt;
&lt;td&gt;SOC 2 Type II, ISO 27001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit Logs&lt;/td&gt;
&lt;td&gt;Can we see who accessed transcripts?&lt;/td&gt;
&lt;td&gt;Yes, with export capability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deletion&lt;/td&gt;
&lt;td&gt;How do you verify data deletion?&lt;/td&gt;
&lt;td&gt;Documented process with confirmation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breach Response&lt;/td&gt;
&lt;td&gt;What's your incident response plan?&lt;/td&gt;
&lt;td&gt;Published policy with timelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Tier 3: Nice-to-Have (Differentiate top vendors)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Enterprise Feature&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Private Hosting&lt;/td&gt;
&lt;td&gt;Can we use our own cloud?&lt;/td&gt;
&lt;td&gt;Yes, supports private deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key Management&lt;/td&gt;
&lt;td&gt;Can we manage encryption keys?&lt;/td&gt;
&lt;td&gt;Customer-managed keys supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Access&lt;/td&gt;
&lt;td&gt;Can we automate compliance checks?&lt;/td&gt;
&lt;td&gt;Full API for audit automation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom Retention&lt;/td&gt;
&lt;td&gt;Per-meeting retention settings?&lt;/td&gt;
&lt;td&gt;Yes, granular control&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Industry-Specific Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Healthcare (HIPAA)
&lt;/h3&gt;

&lt;p&gt;If transcripts contain Protected Health Information (PHI):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BAA Required&lt;/strong&gt;: Must have Business Associate Agreement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access Controls&lt;/strong&gt;: Minimum necessary access principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit Requirements&lt;/strong&gt;: 6-year retention of access logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breach Notification&lt;/strong&gt;: 60-day notification requirement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Most consumer AI transcription services are NOT HIPAA compliant.&lt;/strong&gt; Enterprise tiers with BAAs are required for PHI.&lt;/p&gt;

&lt;p&gt;See our &lt;a href="https://brasstranscripts.com/blog/healthcare-documentation-ai-hipaa-compliant-transcription" rel="noopener noreferrer"&gt;Healthcare AI Transcription HIPAA Guide&lt;/a&gt; for detailed requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Financial Services
&lt;/h3&gt;

&lt;p&gt;If transcripts involve client financial discussions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SEC Recordkeeping&lt;/strong&gt;: Retention requirements for communications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FINRA Supervision&lt;/strong&gt;: Supervisory review obligations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Confidentiality&lt;/strong&gt;: Fiduciary duty to protect information&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor Due Diligence&lt;/strong&gt;: Regulatory expectation for third-party risk&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Legal
&lt;/h3&gt;

&lt;p&gt;If transcripts involve attorney-client communications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privilege Risk&lt;/strong&gt;: Transcription may waive privilege if improperly secured&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Work Product&lt;/strong&gt;: AI-generated summaries may be discoverable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conflicts&lt;/strong&gt;: Multi-matter access controls essential&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ethical Rules&lt;/strong&gt;: State bar requirements for data security&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Education (FERPA)
&lt;/h3&gt;

&lt;p&gt;If transcripts involve student information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Directory Information&lt;/strong&gt;: Limited disclosure without consent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Education Records&lt;/strong&gt;: Strict access controls required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parental Rights&lt;/strong&gt;: Access rights for parents of minors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor Agreements&lt;/strong&gt;: FERPA-compliant data agreements needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  BrassTranscripts Privacy Approach
&lt;/h2&gt;

&lt;p&gt;BrassTranscripts takes a minimalist approach to data retention:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;24-Hour Audio Deletion&lt;/strong&gt;: Source audio files are permanently deleted within 24 hours of upload. We don't retain recordings for quality assurance, training, or any other purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;48-Hour Transcript Deletion&lt;/strong&gt;: Transcript text is permanently deleted within 48 hours. Download your files immediately—they won't be available after this window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Account Required&lt;/strong&gt;: You can transcribe files without creating an account. No email collection, no profile data, no usage tracking tied to identity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No AI Training&lt;/strong&gt;: Your data is not used to train or improve our AI models. Period.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Processing Only&lt;/strong&gt;: We process your file, deliver the transcript, and delete everything. No long-term storage, no searchable archives, no meeting history.&lt;/p&gt;

&lt;h3&gt;
  
  
  When This Approach Works
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;One-time transcription projects&lt;/strong&gt;: Research interviews, podcast episodes, archived recordings&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Privacy-sensitive content&lt;/strong&gt;: When you need transcription without cloud storage&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Compliance simplicity&lt;/strong&gt;: Minimal data retention reduces compliance burden&lt;/p&gt;

&lt;h3&gt;
  
  
  When This Approach Doesn't Work
&lt;/h3&gt;

&lt;p&gt;❌ &lt;strong&gt;Meeting history access&lt;/strong&gt;: No archive to search past transcripts&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Team collaboration&lt;/strong&gt;: No shared workspace or permissions&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Integration needs&lt;/strong&gt;: No CRM, calendar, or tool integrations&lt;/p&gt;

&lt;p&gt;The tradeoff is intentional: maximum privacy means minimum features. If you need collaboration and integrations, enterprise services with longer retention are designed for that use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What's the safest AI transcription option?
&lt;/h3&gt;

&lt;p&gt;"Safe" depends on your threat model. For minimal data exposure, choose services with short retention periods and no AI training use. For enterprise compliance, choose vendors with SOC 2 certification, BAA availability, and configurable retention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can AI transcription services read my transcripts?
&lt;/h3&gt;

&lt;p&gt;Typically, vendor employees can access customer data for support and troubleshooting unless explicitly restricted. Enterprise contracts often include provisions limiting employee access. Review the vendor's privacy policy and ask about internal access controls.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens to my data if the vendor is acquired or goes bankrupt?
&lt;/h3&gt;

&lt;p&gt;Data handling during corporate transactions varies. Most privacy policies include provisions allowing data transfer to successors. Review terms of service for acquisition clauses and consider data portability in vendor evaluation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are open-source transcription tools more secure?
&lt;/h3&gt;

&lt;p&gt;Self-hosted open-source tools (like running Whisper locally) eliminate third-party data exposure but require security expertise to configure properly. Misconfigured self-hosted systems can be less secure than managed services. Evaluate your team's capability honestly.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I audit what's been transcribed in my organization?
&lt;/h3&gt;

&lt;p&gt;Most enterprise transcription services provide admin dashboards showing transcription activity. For services without central management, you may have limited visibility. Consider requiring approval workflows before deploying transcription organization-wide.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://brasstranscripts.com/blog/healthcare-documentation-ai-hipaa-compliant-transcription" rel="noopener noreferrer"&gt;Healthcare AI Transcription: HIPAA Compliance Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://brasstranscripts.com/blog/microsoft-teams-transcription-ownership-problem" rel="noopener noreferrer"&gt;Meeting Transcription Security Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://brasstranscripts.com/blog/why-transcription-services-dont-need-account-creation" rel="noopener noreferrer"&gt;Data Retention Policies Explained&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Need transcription with minimal data retention?&lt;/strong&gt; &lt;a href="https://brasstranscripts.com" rel="noopener noreferrer"&gt;BrassTranscripts&lt;/a&gt; deletes audio within 24 hours and transcripts within 48 hours. No account required, no subscription commitment.&lt;/p&gt;

</description>
      <category>aitranscription</category>
      <category>audiotranscription</category>
      <category>transcriptiontroubleshooting</category>
    </item>
    <item>
      <title>Site Migration SEO Checklist: Save Your Traffic</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Wed, 30 Sep 2026 17:57:24 +0000</pubDate>
      <link>https://dev.to/coppersundev/site-migration-seo-checklist-save-your-traffic-365e</link>
      <guid>https://dev.to/coppersundev/site-migration-seo-checklist-save-your-traffic-365e</guid>
      <description>&lt;p&gt;The most common SEO disaster is an avoidable one: a site migration that loses 30–50% of organic traffic in the first month.&lt;/p&gt;

&lt;p&gt;The losses aren't caused by the new design or the new platform. They're caused by broken redirects, missing meta data, and indexing gaps that nobody caught before launch. A site that took 18 months to build to 50,000 monthly visitors can lose half that traffic in 30 days when the migration runs sloppy. The traffic often takes 6–12 months to fully recover. Some never does.&lt;/p&gt;

&lt;p&gt;This post is the pre-migration, migration-day, and post-migration checklist that protects what you've built. Findings draw on Brass-SEO's audits of post-migration sites in 2026 and Google's published migration guidance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Navigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why Migrations Go Wrong&lt;/li&gt;
&lt;li&gt;The 30 Days Before Migration&lt;/li&gt;
&lt;li&gt;The Week of Migration&lt;/li&gt;
&lt;li&gt;Migration Day&lt;/li&gt;
&lt;li&gt;The First 7 Days After&lt;/li&gt;
&lt;li&gt;The First 90 Days After&lt;/li&gt;
&lt;li&gt;Common Migration Disasters and Recovery&lt;/li&gt;
&lt;li&gt;Frequently Asked Questions&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Migrations Go Wrong
&lt;/h2&gt;

&lt;p&gt;Site migrations fail SEO for one of four reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Missing or broken redirects.&lt;/strong&gt; Old URLs return 404 instead of 301-redirecting to the new equivalent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lost metadata.&lt;/strong&gt; Title tags, meta descriptions, and structured data don't carry over to the new platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indexing gaps.&lt;/strong&gt; Robots.txt accidentally blocks the new site, or &lt;code&gt;noindex&lt;/code&gt; tags persist from the staging environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance regressions.&lt;/strong&gt; New site loads slower than old, Core Web Vitals scores drop, mobile experience degrades.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each one is preventable with explicit checks. The checklists below cover all four.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 30 Days Before Migration
&lt;/h2&gt;

&lt;p&gt;The most important work happens before migration day. Three checklists:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inventory the existing site:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Export a full list of indexed URLs from Google Search Console: Performance → Pages, export all pages with impressions over the past 12 months&lt;/li&gt;
&lt;li&gt;Crawl the existing site with a tool like Screaming Frog or Sitebulb. Export the full URL list with status codes, titles, and meta descriptions&lt;/li&gt;
&lt;li&gt;Identify the top 50–100 pages by impressions. These are the highest-priority pages for redirect mapping and metadata preservation&lt;/li&gt;
&lt;li&gt;Pull the top 100 inbound links from GSC: Links → Top linking pages. These are the URLs that absolutely must redirect correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Plan the redirect map:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For every old URL on the existing site, identify the new URL it should map to&lt;/li&gt;
&lt;li&gt;Old URLs without a clear new equivalent should redirect to the most relevant new page (not the homepage)&lt;/li&gt;
&lt;li&gt;Build the redirect map as a spreadsheet with columns: old URL, new URL, redirect type (almost always 301), priority&lt;/li&gt;
&lt;li&gt;Plan for at least 1.5x the URLs you initially identified — staging crawls often miss pages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pre-stage the new site:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build the new site at a staging URL with &lt;code&gt;noindex&lt;/code&gt; and password protection&lt;/li&gt;
&lt;li&gt;Verify every top-50 page has appropriate metadata configured: title tag, meta description, canonical URL, OpenGraph image&lt;/li&gt;
&lt;li&gt;Confirm structured data is configured on the new platform (Article schema, Product schema, LocalBusiness schema as applicable)&lt;/li&gt;
&lt;li&gt;Test the redirect logic with at least 20 sample URLs from the redirect map&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Week of Migration
&lt;/h2&gt;

&lt;p&gt;Five days out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Re-crawl the existing site&lt;/strong&gt; to catch any pages added since the inventory crawl. Add them to the redirect map&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify the new site's robots.txt&lt;/strong&gt; is configured correctly for production (will allow crawling) and that &lt;code&gt;noindex&lt;/code&gt; tags are scoped only to the staging environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up monitoring&lt;/strong&gt;: GSC for both old and new domains (if changing domain), GA4 for the new site, log monitoring for 404 spikes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notify stakeholders&lt;/strong&gt;. Migration is not a quiet weekend job. Operations, customer support, and marketing should know the timing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two days out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run the redirect map through a redirect checker&lt;/strong&gt; to verify the logic. Tools like Screaming Frog can batch-test redirects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm the new site's XML sitemap&lt;/strong&gt; is generated and accessible at the standard path (&lt;code&gt;/sitemap.xml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify Google Analytics 4 tracking&lt;/strong&gt; is firing on the new staging site&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One day out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Final QA pass&lt;/strong&gt; on the new site. Top 20 pages, manually checked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm DNS propagation strategy.&lt;/strong&gt; If changing hosts, plan the DNS change for low-traffic hours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backup the old site&lt;/strong&gt; completely. You may need to roll back if migration day goes wrong&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Migration Day
&lt;/h2&gt;

&lt;p&gt;The migration itself happens in a specific order. Don't shortcut the sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Take the old site live as-is&lt;/strong&gt; until the new site is verified live. Don't break the old site before the new one is confirmed working.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy the new site.&lt;/strong&gt; Remove &lt;code&gt;noindex&lt;/code&gt; and password protection. Verify the production version loads correctly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Activate the redirect map.&lt;/strong&gt; Either via server-side configuration (Apache &lt;code&gt;.htaccess&lt;/code&gt;, Nginx config, platform-level redirects) or via the new platform's redirect manager (Webflow, WordPress, Shopify all have these).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test the top 20 redirect mappings manually.&lt;/strong&gt; Type old URLs into a browser. Verify they 301-redirect to the correct new URLs. Verify the new URLs return 200 with the correct content.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Submit the new sitemap to Google Search Console.&lt;/strong&gt; Sitemaps report → Add a new sitemap.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Submit a Change of Address in GSC&lt;/strong&gt; if you're moving to a new domain. Settings → Change of address. This is the official signal to Google that domain authority should transfer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run a fresh crawl of the new site.&lt;/strong&gt; Screaming Frog or Sitebulb. Look for 404s, broken internal links, missing canonical tags, missing metadata. Fix anything found before end of day.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The First 7 Days After
&lt;/h2&gt;

&lt;p&gt;Daily checks for the first week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GSC Coverage report.&lt;/strong&gt; Watch for spikes in errors, especially "Soft 404" and "Excluded" categories&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GSC Performance report.&lt;/strong&gt; Compare the new site's impressions against the prior week's baseline. Some drop is normal during reindexing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GA4 sessions.&lt;/strong&gt; Watch the trend. A 10–20% session drop in the first 3 days is normal as the new URLs propagate. A 50%+ drop is a problem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server logs for 404 spikes.&lt;/strong&gt; Pages that show many 404s are URLs missing from the redirect map. Add them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual checks&lt;/strong&gt; of high-priority pages every day. Top 20 pages, top 10 inbound link targets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the broader question of how to read GSC during a migration window, see &lt;a href="https://brass-seo.com/blog/how-to-read-gsc-search-appearance-report" rel="noopener noreferrer"&gt;How to Read GSC's Search Appearance Report&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The First 90 Days After
&lt;/h2&gt;

&lt;p&gt;Weekly checks for the first 90 days:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GSC Coverage report.&lt;/strong&gt; Track the "Indexed" page count vs. the pre-migration baseline. Recovery to baseline typically takes 30–60 days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GSC Performance report.&lt;/strong&gt; Total impressions and clicks should recover to within 90%+ of pre-migration levels by day 30. Faster recovery is possible; slower recovery suggests an unresolved issue&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GA4 organic sessions.&lt;/strong&gt; Same trend. Compare against pre-migration baselines weekly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top 10 keywords by rank.&lt;/strong&gt; If specific high-value rankings dropped, investigate the corresponding pages for missing metadata or redirect issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backlink retention.&lt;/strong&gt; Pull GSC's Top linking sites report monthly for the first 3 months. Inbound links to old URLs should be flowing through the 301 redirects to new URLs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By day 90, traffic should be at or above pre-migration levels. If it's still 20%+ below, the migration has unresolved issues — typically broken redirects on inbound-link URLs or missed metadata on high-traffic pages.&lt;/p&gt;

&lt;p&gt;For more on the timeline patterns, see &lt;a href="https://brass-seo.com/blog/how-long-does-seo-take" rel="noopener noreferrer"&gt;How Long Does SEO Take? What the Data Shows&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Migration Disasters and Recovery
&lt;/h2&gt;

&lt;p&gt;Five disasters Brass-SEO has seen most often:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Robots.txt left at staging configuration.&lt;/strong&gt; Site goes live with &lt;code&gt;User-agent: * / Disallow: /&lt;/code&gt;. Google de-indexes everything within a week. Recovery: fix robots.txt, request re-crawl in GSC. Full recovery usually 30–60 days.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redirect chain instead of single redirect.&lt;/strong&gt; Old URL → intermediate URL → new URL. Each chain hop loses a small amount of link equity. Recovery: collapse chains to single 301 redirects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;301 redirects done as 302 (temporary).&lt;/strong&gt; 302s don't pass link equity. Recovery: change all migration redirects to 301 in the server or platform config.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lost meta descriptions on hundreds of pages.&lt;/strong&gt; The new platform doesn't migrate the meta description field. Pages show Google-generated snippets instead of curated meta descriptions. Recovery: bulk-import metadata from the old site's export.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sitemap submitted with old URLs.&lt;/strong&gt; New sitemap accidentally points to old URLs. Recovery: regenerate sitemap from new platform, resubmit to GSC, request re-crawl.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each one is recoverable. None should happen if the checklists above are followed.&lt;/p&gt;

&lt;p&gt;For the broader Brass-SEO approach to post-migration page audits, see &lt;a href="https://brass-seo.com/page-audits" rel="noopener noreferrer"&gt;Brass-SEO page audits&lt;/a&gt;. To run a pre-migration audit on your current site, &lt;a href="https://brass-seo.com/dashboard" rel="noopener noreferrer"&gt;start a trial&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Should I migrate during a low-traffic period?
&lt;/h3&gt;

&lt;p&gt;Yes when possible. Off-hours migration reduces user impact. Off-season migration (for seasonal businesses) limits the revenue exposure of any unexpected issues. Avoid migrating during peak revenue periods.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long should I keep old redirects active?
&lt;/h3&gt;

&lt;p&gt;Indefinitely for high-traffic and high-link-equity URLs. The cost of maintaining 301 redirects is trivial; the cost of losing inbound link equity from a removed redirect is significant. Some businesses keep migration redirects active for the life of the new site.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if I'm changing domains as well as platforms?
&lt;/h3&gt;

&lt;p&gt;The work doubles in complexity. Add Change of Address in GSC for the domain change, plus all the standard migration checklist items. Plan for a longer recovery window (90–120 days instead of 30–60). Consider migrating the platform first and the domain second (or vice versa) rather than both at once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Brass-SEO help with migration?
&lt;/h3&gt;

&lt;p&gt;Brass-SEO surfaces post-migration traffic data and helps identify pages losing traffic so you can investigate redirect or metadata issues quickly. It doesn't execute the migration itself. For active migration planning, use Brass-SEO alongside dedicated migration tools (Screaming Frog for crawling, GSC for monitoring).&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the biggest mistake teams make on migration day?
&lt;/h3&gt;

&lt;p&gt;Cutting the QA pass short to hit a launch deadline. Migrations that ship at 70% QA-complete reliably produce post-launch traffic disasters that take months to recover. Delay launch by 2–4 days if needed to finish QA. The delay is cheaper than the recovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  More on Content Architecture
&lt;/h2&gt;

&lt;p&gt;Redirects are one step in a migration. For the standalone deep-dive on 301s versus 302s and how to map them correctly, see &lt;a href="https://brass-seo.com/blog/301-redirects-complete-guide" rel="noopener noreferrer"&gt;301 vs 302 Redirects: The Complete Guide&lt;/a&gt;, part of &lt;a href="https://brass-seo.com/content-architecture" rel="noopener noreferrer"&gt;Content Architecture&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>seo</category>
    </item>
    <item>
      <title>SQL Injection in AI-Generated Code, by the Numbers</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Mon, 21 Sep 2026 00:16:31 +0000</pubDate>
      <link>https://dev.to/coppersundev/sql-injection-in-ai-generated-code-by-the-numbers-1bde</link>
      <guid>https://dev.to/coppersundev/sql-injection-in-ai-generated-code-by-the-numbers-1bde</guid>
      <description>&lt;p&gt;One in five. That's how many AI-generated code snippets that touch a database still ship a SQL injection hole, according to Veracode's 2025 testing of more than 100 language models. SQL was the category the models handled best. They still failed it a fifth of the time.&lt;/p&gt;

&lt;p&gt;SQL injection is not an exotic bug. It's CWE-89, ranked third on the 2024 CWE Top 25, and the flagship weakness in Injection, the category that sits third on the OWASP Top 10. It has a fix that every major web framework ships by default. Your AI assistant writes the vulnerable form anyway, because a string-built query returns the right rows in testing and only breaks when a stranger sends a quote character.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers on AI-Generated SQL Injection
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats SQL injection as a measured risk in AI-generated code, not a hypothetical. Veracode's 2025 testing found AI models pass SQL-handling security checks 80% of the time, which leaves one in five database snippets vulnerable, and a 2022 IEEE study measured a 37.35% vulnerability rate across GitHub Copilot's SQL-related completions.&lt;/p&gt;

&lt;p&gt;Veracode's &lt;a href="https://www.veracode.com/blog/genai-code-security-report/" rel="noopener noreferrer"&gt;2025 GenAI Code Security Report&lt;/a&gt; ran more than 100 models across 80 coding tasks in four languages and four vulnerability classes. Across the whole set, 45% of the generated code failed security tests and introduced an OWASP Top 10 weakness. SQL injection was the class the models handled best. Veracode still reported an &lt;a href="https://www.veracode.com/blog/ai-generated-code-security-risks/" rel="noopener noreferrer"&gt;80% security pass rate on that task&lt;/a&gt;, and called the remaining 20% a significant risk for database-driven applications.&lt;/p&gt;

&lt;p&gt;The academic record runs further back. In &lt;a href="https://arxiv.org/abs/2108.09293" rel="noopener noreferrer"&gt;Asleep at the Keyboard?&lt;/a&gt;, a 2022 IEEE Symposium on Security and Privacy paper, researchers generated 1,689 Copilot programs across 89 scenarios and found roughly 40% of them vulnerable. When they narrowed to SQL injection and varied the prompt 17 ways, 152 of 407 valid programs carried the flaw. That's 37.35%. They chose CWE-89 for the experiment because its verdict is unambiguous: a query is either open to injection or it isn't, with no grey zone to argue about.&lt;/p&gt;

&lt;p&gt;Injection didn't earn its ranking by accident. &lt;a href="https://top10.owasp.org/2021/A03_2021-Injection/index.html" rel="noopener noreferrer"&gt;OWASP's A03:2021 category&lt;/a&gt; reports that 94% of tested applications were probed for some form of injection, and MITRE's &lt;a href="https://cwe.mitre.org/data/definitions/89.html" rel="noopener noreferrer"&gt;CWE-89 entry&lt;/a&gt; places it in the Top 25 most dangerous weaknesses. The full evidence set, with every source, sits in BrassCoders's &lt;a href="https://coppersun.dev/research/sql-injection/" rel="noopener noreferrer"&gt;SQL injection research index&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI Assistants Reach for String-Built SQL
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats string-built SQL as a structural symptom of how a model generates code, not a rare slip. Asked for a function that looks up a user by email, the model aims for a query that returns the right row on the developer's test input, and dropping the email straight into the query string is the shortest path to that goal.&lt;/p&gt;

&lt;p&gt;Consider the two ways to write the same lookup. The vulnerable form is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE email = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The safe form is barely longer, and it hands the value to the driver separately from the SQL text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE email = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both return the same row for &lt;code&gt;alice@example.com&lt;/code&gt;. Only the first one also returns every row in the table when someone sends &lt;code&gt;' OR '1'='1&lt;/code&gt;. Nothing in the prompt rewarded the second version, and nothing in the developer's quick test exercised the input that separates them. This is the same gap that produces every other class of AI-generated bug: the model satisfies the stated goal, and the adversarial input was never part of the goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Static Analysis Catches
&lt;/h2&gt;

&lt;p&gt;BrassCoders bundles Bandit, and Bandit ships a dedicated check, B608, for exactly this shape. As one of the 12 scanners in a BrassCoders scan, it flags the pattern before the query ever runs against real input.&lt;/p&gt;

&lt;p&gt;Bandit's &lt;a href="https://bandit.readthedocs.io/en/latest/plugins/b608_hardcoded_sql_expressions.html" rel="noopener noreferrer"&gt;B608 documentation&lt;/a&gt; describes the check plainly: it looks for strings that resemble SQL statements involved in some form of string-building operation. The f-string lookup above matches that description exactly. So does a query assembled with &lt;code&gt;+&lt;/code&gt; concatenation or &lt;code&gt;%&lt;/code&gt; formatting, or one built up across several lines before it reaches &lt;code&gt;cursor.execute&lt;/code&gt;. A static rule catches the structural marker, the SQL keywords sitting next to a string operation, without executing anything.&lt;/p&gt;

&lt;p&gt;That structural detection is also the boundary of what a scanner can know on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Static Analysis Can't Decide
&lt;/h2&gt;

&lt;p&gt;BrassCoders reports the raw B608 pattern match and stops there. Whether the interpolated value is actually attacker-controlled, reachable from an untrusted request, or already escaped upstream is a source-context judgment BrassCoders leaves to the AI assistant reading its YAML output rather than inferring itself.&lt;/p&gt;

&lt;p&gt;The distinction matters because B608 fires on shape, not on reachability. A query built from a hardcoded table name the developer controls is a false positive. The identical pattern built from &lt;code&gt;request.args.get("email")&lt;/code&gt; is a live vulnerability. A deterministic scanner that tried to tell those apart would have to trace the value back through the call graph and decide whether the source is trusted, and if it guessed wrong, it would either bury a real bug or cry wolf on a safe one. BrassCoders emits the finding with its file, line, and type; Claude Code or Cursor reads the surrounding code and decides whether this specific match is exploitable. BrassCoders is the pattern reporter and the AI assistant is the triage layer, the same division of labor a BrassCoders scan applies to every finding class.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix the Framework Already Ships
&lt;/h2&gt;

&lt;p&gt;BrassCoders points every SQL injection finding back to the fix the language and framework vendors already document: parameterized queries. The safe form hands user input to the database driver as data, so it can never be read as SQL.&lt;/p&gt;

&lt;p&gt;Python's own &lt;a href="https://docs.python.org/3/library/sqlite3.html" rel="noopener noreferrer"&gt;sqlite3 documentation&lt;/a&gt; warns against assembling queries with string operations and tells developers to always use placeholders to bind values, precisely so an attacker can't close a quote and inject their own clause.&lt;/p&gt;

&lt;p&gt;Frameworks make the safe path the default. &lt;a href="https://docs.djangoproject.com/en/5.2/topics/security/" rel="noopener noreferrer"&gt;Django's security documentation&lt;/a&gt; explains that its querysets are protected from SQL injection because the query's SQL is defined separately from its parameters, and the database driver escapes anything user-provided. The risk returns the moment generated code steps outside the ORM to write raw SQL, which is exactly what an assistant does when a prompt asks for a query the ORM makes awkward. That's the line worth a second look on any AI-written data-access change: not whether a query exists, but whether it was built by hand out of strings.&lt;/p&gt;

&lt;p&gt;Install BrassCoders and get Bandit's B608 check running as one of 12 scanners on every commit: &lt;code&gt;pip install brasscoders&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>benchmarking</category>
    </item>
    <item>
      <title>Will Your AI Write Tests That Catch Real Bugs?</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Mon, 21 Sep 2026 00:15:26 +0000</pubDate>
      <link>https://dev.to/coppersundev/will-your-ai-write-tests-that-catch-real-bugs-4pj3</link>
      <guid>https://dev.to/coppersundev/will-your-ai-write-tests-that-catch-real-bugs-4pj3</guid>
      <description>&lt;p&gt;A test suite can execute 90% of your code and verify none of it. Coverage counts the lines a test runs. It says nothing about whether the test would notice if one of those lines returned the wrong answer.&lt;/p&gt;

&lt;p&gt;That gap is where AI-generated tests live. Ask an assistant for tests and it produces something that runs, imports cleanly, and lights up the coverage report. Whether any of it would catch a real bug is a separate question, and the research says the answer is often no.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coverage Measures Execution, Not Verification
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats a coverage percentage as an execution count, not a quality signal. Coverage records which lines a test ran; it says nothing about whether the test would fail if those lines returned the wrong answer.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://coverage.readthedocs.io/en/latest/index.html" rel="noopener noreferrer"&gt;coverage.py documentation&lt;/a&gt; is explicit that the tool monitors which parts of the code have been executed by tests, and identifies code that could have been executed but was not. It makes no claim about whether executed code was checked for correct behavior.&lt;/p&gt;

&lt;p&gt;The mutation-testing project PIT puts the distinction in one sentence. &lt;a href="https://pitest.org/" rel="noopener noreferrer"&gt;Its documentation&lt;/a&gt; states that traditional coverage measures only which code is executed by your tests, and does not check that your tests are actually able to detect faults in that code. A test that calls &lt;code&gt;parse_date(input)&lt;/code&gt; and asserts nothing executes every line inside &lt;code&gt;parse_date&lt;/code&gt;. The coverage report credits all of them. The test would still pass if &lt;code&gt;parse_date&lt;/code&gt; returned the wrong date, the wrong type, or &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Peer-Reviewed Verdict on Coverage
&lt;/h2&gt;

&lt;p&gt;BrassCoders leans on the largest study of the question to date. Inozemtseva and Holmes generated 31,000 test suites across five systems totaling 724,000 lines of code, and found only a low-to-moderate correlation between coverage and a suite's fault-detection power once the number of test cases is held constant.&lt;/p&gt;

&lt;p&gt;Their &lt;a href="https://www.cs.ubc.ca/~rtholmes/papers/icse_2014_inozemtseva.pdf" rel="noopener noreferrer"&gt;ICSE 2014 paper&lt;/a&gt; states the takeaway directly: coverage is useful for finding under-tested code, but it should not be used as a quality target because it is not a good indicator of test suite effectiveness. Stronger coverage criteria didn't rescue the signal either; branch coverage was no better a predictor than statement coverage.&lt;/p&gt;

&lt;p&gt;A companion result points at what does predict fault detection. Zhang and Mesbah composed 6,700 test suites from 24,000 assertions across five real-world Java projects and found, in a paper deliberately titled &lt;a href="https://people.ece.ubc.ca/amesbah/resources/papers/fse15.pdf" rel="noopener noreferrer"&gt;Assertions Are Strongly Correlated with Test Suite Effectiveness&lt;/a&gt;, that the number of assertions strongly correlates with a suite's effectiveness. The pair of findings reads cleanly together. Lines executed is a weak signal. Assertions made is a strong one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens When AI Writes the Tests
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats AI-generated tests as coverage-rich and assertion-poor by default. A 2023 study of ChatGPT-generated unit tests found only 24.8% ran without execution errors, and 17.3% compiled but failed on the model's own incorrect assertions.&lt;/p&gt;

&lt;p&gt;Those numbers come from &lt;a href="https://arxiv.org/abs/2305.04207" rel="noopener noreferrer"&gt;an empirical evaluation of ChatGPT for unit-test generation&lt;/a&gt;, which generated 1,000 tests and reported that 42.1% compiled while just under a quarter executed cleanly. The assertions were the recurring problem: the model wrote checks that looked plausible and encoded the wrong expected value. A test with a wrong assertion is worse than no test, because it turns green and tells you the behavior is confirmed.&lt;/p&gt;

&lt;p&gt;Coverage figures for AI tests swing wildly with the codebase. Siddiq and colleagues, in &lt;a href="https://arxiv.org/abs/2305.00418" rel="noopener noreferrer"&gt;an empirical study of LLM-generated JUnit tests&lt;/a&gt;, measured a Codex model above 80% coverage on the HumanEval benchmark and under 2% on the real-world SF110 corpus. They also catalogued the tests' contents and found named test smells, Empty Tests and Duplicated Asserts among them, patterns that raise coverage while checking nothing.&lt;/p&gt;

&lt;p&gt;The generators are genuinely good at the coverage number. TestPilot, evaluated on 25 npm packages in &lt;a href="https://arxiv.org/abs/2302.06527" rel="noopener noreferrer"&gt;a 2024 IEEE Transactions on Software Engineering study&lt;/a&gt;, reached a median 70.2% statement coverage. Hitting the number is the thing they do well, which is exactly why the number flatters them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Measure Whether Tests Actually Verify
&lt;/h2&gt;

&lt;p&gt;BrassCoders points teams to mutation testing as the check coverage can't perform. A mutation tool seeds small faults into the code, a flipped comparison or a changed constant, then runs the suite and reports how many faults the tests caught. A test that asserts nothing catches none of them, no matter how much code it executes.&lt;/p&gt;

&lt;p&gt;Mutation score is a validated proxy for real-bug detection, not a lab curiosity. Just and colleagues, in &lt;a href="https://homes.cs.washington.edu/~rjust/publ/mutants_real_faults_fse_2014.pdf" rel="noopener noreferrer"&gt;Are Mutants a Valid Substitute for Real Faults in Software Testing?&lt;/a&gt;, studied 357 real faults across 321,000 lines of code and found a statistically significant correlation between mutant detection and real-fault detection, independent of code coverage. PIT is the reference tool on the JVM; Python projects have mutmut and Cosmic Ray. Run one against an AI-written test file and the assertion-free tests reveal themselves immediately, because they kill almost nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where BrassCoders Fits
&lt;/h2&gt;

&lt;p&gt;BrassCoders does not run your tests or grade them; it scans the code the tests are supposed to protect. A static analysis pass flags a real SQL injection or an unsafe deserialization call whether or not the test suite covers that line, so a green, high-coverage suite full of hollow assertions can't hide a finding BrassCoders would report.&lt;/p&gt;

&lt;p&gt;That makes static analysis an independent signal from the test suite, and independence is the point. Coverage and mutation score measure how good your tests are. A BrassCoders scan measures the code itself, reading it for the bug patterns AI assistants tend to introduce, and it reaches the same verdict on a file with zero tests as on one reporting 100% coverage. The full evidence set behind this post, with every source, is in BrassCoders's &lt;a href="https://coppersun.dev/research/ai-test-quality/" rel="noopener noreferrer"&gt;AI test quality research index&lt;/a&gt;. BrassCoders emits the pattern matches as YAML; the AI assistant reading that file decides which ones matter in context, the same division of labor a scan applies to every finding.&lt;/p&gt;

&lt;p&gt;None of this means you should stop asking an assistant for tests. It means the coverage number it produces isn't the thing to trust. Measure the tests with mutation testing, and scan the code with a tool that doesn't care what the tests claim.&lt;/p&gt;

&lt;p&gt;BrassCoders scans your code for the bugs a hollow test suite misses, as one of 12 scanners on every commit: &lt;code&gt;pip install brasscoders&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>engineering</category>
    </item>
    <item>
      <title>What BrassCoders Catches in OWASP PyGoat</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Wed, 16 Sep 2026 17:34:54 +0000</pubDate>
      <link>https://dev.to/coppersundev/what-brasscoders-catches-in-owasp-pygoat-1c6b</link>
      <guid>https://dev.to/coppersundev/what-brasscoders-catches-in-owasp-pygoat-1c6b</guid>
      <description>&lt;p&gt;Four months ago, BrassCoders published a gap list for OWASP PyGoat: four documented vulnerabilities it didn't yet catch. A SQL injection. An insecure deserialization. A command injection reachable through tainted data. A call to &lt;code&gt;eval()&lt;/code&gt; that PyGoat exists specifically to demonstrate. Running the same scan today, against the same pinned commit, catches all four.&lt;/p&gt;

&lt;h2&gt;
  
  
  What PyGoat Is, and Why It's a Fair Test
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/adeyosemanputra/pygoat" rel="noopener noreferrer"&gt;PyGoat&lt;/a&gt; is a Django application OWASP contributors built to demonstrate the OWASP Top 10 vulnerability classes in real, runnable Python code. Not snippets in a slide deck. A working app with the vulnerabilities wired into actual request handlers, the kind a scanner has to trace through real control flow to find. The project's own README states it plainly: an intentionally vulnerable web application, with the vulnerabilities based on the OWASP Top 10.&lt;/p&gt;

&lt;p&gt;That's what makes it a fair scanner test. Every finding has a known, documented right answer, published by the same people who wrote the vulnerable code. BrassCoders publishes a benchmark page for PyGoat pinned to a specific commit, &lt;code&gt;c11e8429349cc05ff38564d3bf7ef09fb2411874&lt;/code&gt;, the v2.0.1 release tag, so the results below are reproducible by anyone. Not claimed. Checkable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Scan
&lt;/h2&gt;

&lt;p&gt;A single &lt;code&gt;brasscoders --offline scan .&lt;/code&gt; against PyGoat's 203 Python files surfaces 766 raw findings, cuts that to 403 through the OSS core's heuristic pass, and flags 52 as critical or high severity, no account, no network call, all before anything leaves the local machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;brasscoders &lt;span class="nt"&gt;--offline&lt;/span&gt; scan &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="go"&gt;🎺 BrassCoders - Scanning pygoat
⚡ Running 12 scanners in parallel (max_workers=6)...
🧹 Running intelligent optimization...
   Intelligent optimization: 766 → 403 findings (47.4% reduction)
✨ Running AI enrichment...
   Enriched: 403 → 242 findings (161 duplicates dropped)

✅ Analysis complete!
📊 Found 242 total issues
🚨 52 critical/high severity issues require attention
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of what survives the heuristic pass is noise a Django app carries by default: dead code, style nits, a handful of low-confidence privacy matches. Underneath that noise sit the findings that matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What BrassCoders Caught
&lt;/h2&gt;

&lt;p&gt;Eleven vulnerability classes, all traced to the exact file and line PyGoat's maintainers documented:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Critical Findings:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Possible hardcoded credential (critical) — &lt;code&gt;introduction/views.py:857&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Subprocess call with &lt;code&gt;shell=True&lt;/code&gt;, security issue (critical) — &lt;code&gt;introduction/views.py:423&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use of weak MD5 hash for security (critical) — &lt;code&gt;introduction/views.py:1017&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Possible hardcoded credential (critical) — &lt;code&gt;introduction/views.py:859&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Possible hardcoded credential (critical) — &lt;code&gt;introduction/views.py:861&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Four hardcoded credentials, all in the same file, all caught by BrassCoders's &lt;code&gt;auth_pattern_analyzer&lt;/code&gt;. A fifth, at line 529, comes from a different detector entirely, the &lt;code&gt;SecretsScanner&lt;/code&gt;, matching a hardcoded secret keyword rather than a credential pattern. Two detectors, two different signals, the same underlying bug.&lt;/p&gt;

&lt;p&gt;The command injection at line 423 is the textbook &lt;a href="https://cwe.mitre.org/data/definitions/78.html" rel="noopener noreferrer"&gt;CWE-78&lt;/a&gt; shape: &lt;code&gt;subprocess&lt;/code&gt; called with &lt;code&gt;shell=True&lt;/code&gt; against a string built from request input. Bandit catches it directly. A second command-injection path, at line 421, needs more than pattern matching. BrassCoders's Semgrep-based taint scanner traces the tainted value from where it enters the request through to where it reaches the shell, and flags it separately, at &lt;code&gt;critical&lt;/code&gt; severity, distinct from the line-423 finding.&lt;/p&gt;

&lt;p&gt;The weak-hash finding is &lt;a href="https://cwe.mitre.org/data/definitions/327.html" rel="noopener noreferrer"&gt;CWE-327&lt;/a&gt;: PyGoat hashes something security-relevant with MD5, a function with known collision attacks, at line 1017. Bandit's rule ID for this is B324.&lt;/p&gt;

&lt;p&gt;The full set, all eleven, all at &lt;code&gt;introduction/views.py&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Detector&lt;/th&gt;
&lt;th&gt;CWE / Bandit ID&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;857&lt;/td&gt;
&lt;td&gt;hardcoded_credential&lt;/td&gt;
&lt;td&gt;&lt;code&gt;auth_pattern_analyzer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CWE-798&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;859&lt;/td&gt;
&lt;td&gt;hardcoded_credential&lt;/td&gt;
&lt;td&gt;&lt;code&gt;auth_pattern_analyzer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CWE-798&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;861&lt;/td&gt;
&lt;td&gt;hardcoded_credential&lt;/td&gt;
&lt;td&gt;&lt;code&gt;auth_pattern_analyzer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CWE-798&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;863&lt;/td&gt;
&lt;td&gt;hardcoded_credential&lt;/td&gt;
&lt;td&gt;&lt;code&gt;auth_pattern_analyzer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CWE-798&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;529&lt;/td&gt;
&lt;td&gt;hardcoded_credential&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SecretsScanner&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CWE-798&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;423&lt;/td&gt;
&lt;td&gt;command_injection&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;bandit&lt;/code&gt; (B602)&lt;/td&gt;
&lt;td&gt;CWE-78&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;421&lt;/td&gt;
&lt;td&gt;command_injection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SemgrepTaintScanner&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CWE-78&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1017&lt;/td&gt;
&lt;td&gt;weak_crypto&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;bandit&lt;/code&gt; (B324)&lt;/td&gt;
&lt;td&gt;CWE-327&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;155&lt;/td&gt;
&lt;td&gt;sql_injection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bandit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CWE-89&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;211&lt;/td&gt;
&lt;td&gt;deserialization&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bandit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CWE-502&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;451&lt;/td&gt;
&lt;td&gt;code_injection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bandit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CWE-95&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four detectors, six vulnerability categories, one file. &lt;code&gt;views.py&lt;/code&gt; in PyGoat's &lt;code&gt;introduction&lt;/code&gt; app is doing the job it was built for: giving a scanner every OWASP Top 10 pattern in one place, so a single case study can walk through the whole set instead of chasing findings across a dozen files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Confidence Nuance
&lt;/h2&gt;

&lt;p&gt;Not every finding above carries the same weight, and pretending otherwise would undercut the point of publishing real numbers instead of marketing copy. The SQL-injection finding, &lt;a href="https://cwe.mitre.org/data/definitions/89.html" rel="noopener noreferrer"&gt;CWE-89&lt;/a&gt;, at &lt;code&gt;views.py:155&lt;/code&gt;, comes through with Bandit confidence 0.65: low to medium. Bandit flagged a string-based query pattern that looks like the injection shape, but string-built SQL isn't automatically exploitable if the interpolated value never reaches user input.&lt;/p&gt;

&lt;p&gt;BrassCoders surfaces the confidence score precisely so a reviewer doesn't take the severity label at face value. A &lt;code&gt;critical&lt;/code&gt; tag on a low-confidence finding means "worth checking first," not "confirmed." The two remaining new catches read differently: the deserialization finding (&lt;a href="https://cwe.mitre.org/data/definitions/502.html" rel="noopener noreferrer"&gt;CWE-502&lt;/a&gt;, unsafe &lt;code&gt;pickle&lt;/code&gt; use at line 211) and the eval-injection finding (&lt;a href="https://cwe.mitre.org/data/definitions/95.html" rel="noopener noreferrer"&gt;CWE-95&lt;/a&gt;, a bare &lt;code&gt;eval()&lt;/code&gt; call at line 451) both carry Bandit confidence 0.9, high enough to triage first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four Gaps, Closed
&lt;/h2&gt;

&lt;p&gt;Here's the part worth dwelling on. On 2026-05-17, BrassCoders published a baseline scan of this same pinned PyGoat commit, and alongside the findings, a list of what it missed: the SQL injection at line 155, the deserialization at line 211, the eval injection at line 451, and the taint-tracked command injection at line 421. Four documented vulnerabilities, published as gaps, not hidden.&lt;/p&gt;

&lt;p&gt;That baseline is four months old. Re-running the identical scan today, against the identical commit, on BrassCoders 2.0.16, catches all four. Nothing about PyGoat changed; the pinned commit is frozen. What changed is the scanner: a new taint-tracking path for command injection, broader Bandit rule coverage on the deserialization and eval cases, and sharper confidence scoring on the SQL-injection pattern. Four gaps a customer could have read about in May are closed in September, checkable by re-running the same command against the same public page.&lt;/p&gt;

&lt;p&gt;Publishing gaps only means something if the gap list gets shorter over time. This is the first time BrassCoders has a before-and-after on one of its own published gap lists, and the direction is the one that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Doesn't Prove
&lt;/h2&gt;

&lt;p&gt;PyGoat is built to be caught. Every line in it exists because OWASP wanted a scanner to find it, which makes an 11-of-11 result a floor, not a ceiling, for what a scanner should catch here, not a claim about performance on code nobody designed to be vulnerable. BrassCoders publishes separate noise-floor numbers against maintained, non-vulnerable projects, Django, FastAPI, Flask, at &lt;a href="https://coppersun.dev/benchmarks/" rel="noopener noreferrer"&gt;coppersun.dev/benchmarks&lt;/a&gt;, because a benchmark against an intentionally-broken app and a benchmark against a real production codebase answer different questions.&lt;/p&gt;

&lt;p&gt;This scan also isn't a false-positive study. Every finding above is a true positive against PyGoat's documented vulnerability list, but that says nothing about how often BrassCoders flags something that isn't actually a problem on a codebase that wasn't built as an answer key. Django's own real-world scan, published at the same benchmarks page, surfaces 1,608 findings, none of them planted, all of them ordinary code carrying the ordinary weight of a mature framework. That number and this one measure different things. Conflating them would misrepresent both.&lt;/p&gt;

&lt;p&gt;And confidence scores aren't guarantees. The low-confidence SQL-injection catch is real signal worth investigating, not a confirmed exploit. BrassCoders's own &lt;a href="https://coppersun.dev/what-brasscoders-detects/" rel="noopener noreferrer"&gt;interpretation guidance&lt;/a&gt; treats &lt;code&gt;false_positive_likelihood&lt;/code&gt; as a triage heuristic, never as ground truth on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce This Yourself
&lt;/h2&gt;

&lt;p&gt;Four commands reproduce every finding in this post, against the same pinned commit, on your own machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/adeyosemanputra/pygoat.git project
&lt;span class="nb"&gt;cd &lt;/span&gt;project &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git checkout c11e8429349cc05ff38564d3bf7ef09fb2411874
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
brasscoders &lt;span class="nt"&gt;--offline&lt;/span&gt; scan &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check &lt;code&gt;.brass/security_report.yaml&lt;/code&gt; for the findings above, or &lt;code&gt;.brass/ai_instructions.yaml&lt;/code&gt; for the ranked, AI-consumable version. The full, current benchmark page, including the closed-gap history, is at &lt;a href="https://coppersun.dev/benchmarks/pygoat/" rel="noopener noreferrer"&gt;coppersun.dev/benchmarks/pygoat&lt;/a&gt;. Install BrassCoders itself with &lt;code&gt;pip install brasscoders&lt;/code&gt;. The OSS core that ran this entire scan is free, Apache 2.0 licensed, and made zero outbound network calls.&lt;/p&gt;

</description>
      <category>security</category>
      <category>benchmarking</category>
    </item>
    <item>
      <title>Will Your AI Write A Regex That Hangs Your Server?</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Fri, 04 Sep 2026 16:00:59 +0000</pubDate>
      <link>https://dev.to/coppersundev/will-your-ai-write-a-regex-that-hangs-your-server-2n9g</link>
      <guid>https://dev.to/coppersundev/will-your-ai-write-a-regex-that-hangs-your-server-2n9g</guid>
      <description>&lt;p&gt;A regex your AI wrote in half a second can put your server on the floor for 27 minutes. That's not hyperbole: it's what happened to Cloudflare in July 2019, and the mechanism behind it, catastrophic backtracking, has landed real CVEs in &lt;code&gt;ajv&lt;/code&gt; and &lt;code&gt;minimatch&lt;/code&gt; within the last year. CWE-1333 gives the bug a name. The rest of this post gives it a shape you can spot before it ships.&lt;/p&gt;

&lt;p&gt;Ask an AI assistant for a validation regex and it will almost always produce something that works on the examples you gave it. Nothing about that request asks the model to think about the worst input a stranger could send. That gap, between correctness on a sample and safety against an adversary, is where ReDoS lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Catastrophic Backtracking Actually Does
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats catastrophic backtracking as a distinct, adversarial failure mode, not a slow-code problem you'd catch by profiling under normal load. Most regex engines, including the ones built into Python, JavaScript, and Java, match by backtracking: when a pattern fails at one point, the engine rewinds and tries the next possible path through the pattern. A regex with nested quantifiers gives the engine an exploding number of equivalent paths to try before it can conclude a string doesn't match.&lt;/p&gt;

&lt;p&gt;Take the classic bad pattern &lt;code&gt;(a+)+$&lt;/code&gt;. Against the string &lt;code&gt;aaaaaaaaaaaaaaaaaaaaaaaa!&lt;/code&gt;, the trailing &lt;code&gt;!&lt;/code&gt; guarantees the whole thing fails to match. But there are many ways to partition 24 a's among the inner and outer &lt;code&gt;+&lt;/code&gt; quantifiers, and the engine tries a large share of them before giving up. Add one more &lt;code&gt;a&lt;/code&gt; to the input and the work roughly doubles. That's the exponential curve hiding inside a four-character regex.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS" rel="noopener noreferrer"&gt;OWASP's ReDoS reference&lt;/a&gt; calls this shape an evil regex: a group with repetition inside it, where that inner group also repeats or offers overlapping alternatives. &lt;code&gt;(a+)+$&lt;/code&gt;, &lt;code&gt;([a-zA-Z]+)*$&lt;/code&gt;, and &lt;code&gt;(a|aa)+$&lt;/code&gt; all fit the description. None of them look dangerous in a code review. All of them are.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cwe.mitre.org/data/definitions/1333.html" rel="noopener noreferrer"&gt;CWE-1333&lt;/a&gt;, MITRE's formal entry for this weakness, defines it plainly: a regular expression whose worst-case computational complexity is inefficient, possibly exponential, in the length of the input. The consequence it lists isn't data exposure or privilege escalation. It's availability, the same category as a crashed process or a full disk, and that's the detail teams tend to underweight when they triage a ReDoS finding against a severity rubric built around leaked data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 27 Minutes Cloudflare Lost To One Regex Line
&lt;/h2&gt;

&lt;p&gt;BrassCoders points to the Cloudflare outage as the proof this isn't a lab exercise. On July 2, 2019, Cloudflare pushed a new WAF rule to its entire global edge network at once, skipping the gradual rollout it normally uses. The rule's regex hit catastrophic backtracking against live traffic within minutes.&lt;/p&gt;

&lt;p&gt;CPU usage across every server handling HTTP and HTTPS requests spiked toward 100%. The network lost roughly 80% of its traffic. Sites behind Cloudflare, a meaningful fraction of the web at the time, returned 502 errors for 27 minutes before engineers disabled the WAF globally and restored service. &lt;a href="https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019" rel="noopener noreferrer"&gt;Cloudflare's own postmortem&lt;/a&gt;, written by then-CTO John Graham-Cumming and published two weeks later, walks through the simplified pattern that caused it and shows the backtracking step count: a 3-character test string took 23 steps to reject, and a 22-character string took 555. The growth between those two numbers is the entire danger of ReDoS in one comparison.&lt;/p&gt;

&lt;p&gt;Worth noting: a safeguard meant to cap regex CPU time had been removed during an earlier WAF refactor. The bad regex was necessary for the outage. It wasn't sufficient on its own — the missing backstop was what let it take down the whole network instead of one request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI Assistants Reach For The Evil Regex Shape
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats the evil-regex shape as a structural symptom of how an AI assistant generates code, not a rare slip. Asked for a validation pattern, the model optimizes for one goal: match every example the prompt implied. Nothing in that goal accounts for a hostile string, and that gap is exactly what produces a nested-quantifier regex indistinguishable from a safe one until an attacker finds the cliff.&lt;/p&gt;

&lt;p&gt;Validating an email address, parsing a log line, and stripping whitespace from a nested structure all reduce to the same instruction in a prompt: write a pattern that matches these cases. Nesting a quantifier inside a quantifier is often the shortest route to "matches everything I tried," and it costs nothing on the small inputs a developer tests against. The cost only shows up once a stranger controls what gets fed in.&lt;/p&gt;

&lt;p&gt;This is structurally the same gap that produces every other class of AI-generated bug BrassCoders catalogs: the model optimizes for the stated goal, and worst-case behavior was never part of the goal. A performance anti-pattern like an O(N²) loop degrades gracefully as input grows. A ReDoS regex doesn't degrade. It cliffs, going from instant to unresponsive across a narrow range of input lengths, and an attacker only needs to find that cliff once.&lt;/p&gt;

&lt;p&gt;The pattern doesn't stay confined to code an AI wrote from scratch, either. It shows up wherever a regex gets built dynamically, string interpolation into a &lt;code&gt;RegExp&lt;/code&gt; constructor, a schema validator compiling a user-supplied pattern, a glob matcher expanding a wildcard, because none of those code paths look like "I wrote a regex" in a diff.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Advisories Already Landing In Your Dependency Tree
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats two recent GitHub Security Advisories as proof this risk already ships in production, not just in a lab example. One reached a vulnerable regex through a JSON Schema validator's dynamic option; the other generated the vulnerable regex entirely at runtime, with no dangerous pattern ever visible in source. Both turned a crafted string of a few dozen bytes into tens of seconds of CPU time on ordinary hardware.&lt;/p&gt;

&lt;p&gt;Start with &lt;code&gt;ajv&lt;/code&gt;, the JSON Schema validator that sits transitively underneath a large share of published npm packages. It shipped &lt;a href="https://github.com/advisories/GHSA-2g4f-4pwh-qvx6" rel="noopener noreferrer"&gt;CVE-2025-69873&lt;/a&gt;: when its &lt;code&gt;$data&lt;/code&gt; option is enabled, an attacker-influenced pattern reaches the &lt;code&gt;RegExp&lt;/code&gt; constructor unvalidated. Against a pattern shaped like &lt;code&gt;^(a|a)*$&lt;/code&gt;, a 31-character payload produced roughly 44 seconds of CPU blocking. Each additional character in the payload roughly doubled the run time — the same exponential curve as Cloudflare's, just measured on a laptop instead of an edge network.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;minimatch&lt;/code&gt;, the glob-matching library that underpins a large share of Node.js file-handling and CI tooling, shipped &lt;a href="https://github.com/advisories/GHSA-23c5-xmqv-rm74" rel="noopener noreferrer"&gt;CVE-2026-27904&lt;/a&gt; for the opposite reason: the vulnerable regex never appears in anyone's source code at all. Nested extglob patterns like &lt;code&gt;*(*(*(a|b)))&lt;/code&gt; compile into regexes with nested unbounded quantifiers at runtime. A 12-byte pattern against an 18-byte non-matching input stalled the library's default matching function for over 7 seconds; one more level of nesting pushed the stall toward roughly 64 seconds. If your project accepts a user-supplied glob, through a file-upload filter or a CI config field, you inherit that risk from a dependency you never audited a line of.&lt;/p&gt;

&lt;p&gt;Neither of these advisories involved code an AI assistant wrote. They're here because they show the exact same failure shape landing in production, at scale, in libraries downloaded millions of times a week. An AI-generated regex with the same nested-quantifier shape is one crafted input away from the same outcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catching It Before It Ships
&lt;/h2&gt;

&lt;p&gt;BrassCoders bundles Semgrep as one of its 12 scanners, and Semgrep ships a purpose-built analyzer for exactly this weakness class: a rule that flags a catastrophic-backtracking pattern shape without ever executing the regex. It's the same detection engine already doing the SQL-injection and hardcoded-secret work inside a BrassCoders scan, so a team wiring in a custom ReDoS rule isn't reaching for a new tool.&lt;/p&gt;

&lt;p&gt;A regex doesn't need to run against a hostile string to be flagged as risky; the pattern shape alone is enough for static analysis to catch. &lt;a href="https://semgrep.dev/docs/writing-rules/metavariable-analysis" rel="noopener noreferrer"&gt;Semgrep&lt;/a&gt; ships that analyzer under the name &lt;code&gt;redos&lt;/code&gt;, invoked with &lt;code&gt;metavariable-analysis: analyzer: redos&lt;/code&gt; inside a custom rule, and it checks a captured pattern against known anti-pattern shapes.&lt;/p&gt;

&lt;p&gt;JavaScript and TypeScript projects have a second option that requires no extra CI step at all: &lt;a href="https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-unsafe-regex.md" rel="noopener noreferrer"&gt;&lt;code&gt;eslint-plugin-security&lt;/code&gt;&lt;/a&gt;, a widely-used ESLint plugin with over 2,300 GitHub stars, ships a &lt;code&gt;detect-unsafe-regex&lt;/code&gt; rule in its recommended configuration. It flags a regex that could block the Node.js event loop on the same lint pass that already runs on every commit.&lt;/p&gt;

&lt;p&gt;Whichever scanner flags the pattern, the next call is a triage question a deterministic scanner shouldn't try to answer on its own: is this specific regex reachable with attacker-controlled input, and if so, how bad is the exposure? That's a judgment call that needs the surrounding code, not just the pattern text. BrassCoders is built to stay out of that call: it reports the raw pattern match and leaves the reachability analysis to the AI assistant reading its output, the same division of labor it applies to every finding class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mitigating What Static Analysis Can't Guarantee
&lt;/h2&gt;

&lt;p&gt;BrassCoders and every other static scanner can only flag the pattern shape, never the runtime behavior, and no static analyzer can promise it caught every vulnerable shape across a large codebase. The same regex that's a curiosity against a 200-character input becomes a production incident against a 200,000-character one, and a length cap upstream of the regex engine is the difference between the two outcomes.&lt;/p&gt;

&lt;p&gt;No scanner catches every vulnerable pattern; some regexes only reveal their exponential behavior against specific input shapes that static analysis can't enumerate. Cap the length of anything a regex processes before it reaches the regex engine at all. The cap doesn't fix the pattern, but it bounds the blast radius to something a request timeout can absorb.&lt;/p&gt;

&lt;p&gt;For genuinely untrusted input at scale, consider a regex engine that guarantees linear-time matching regardless of pattern shape. RE2 and its language ports trade some regex syntax (backreferences, in particular) for a worst-case bound that makes ReDoS structurally impossible rather than merely unlikely. That's a bigger lift than adding a lint rule, and it's usually reserved for the specific code paths that parse untrusted input directly, not applied wholesale across a codebase.&lt;/p&gt;

&lt;p&gt;None of this replaces reviewing the regex your AI assistant just handed you. A pattern with a quantified group inside another quantified group is worth a second look regardless of what wrote it, and now you know the shape to look for.&lt;/p&gt;

&lt;p&gt;Install BrassCoders and get Semgrep, the engine behind the redos analyzer, running as one of 12 scanners on every commit: &lt;code&gt;pip install brasscoders&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>engineering</category>
    </item>
    <item>
      <title>IDOR and Access Control in AI APIs, by the Numbers</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Fri, 04 Sep 2026 15:59:11 +0000</pubDate>
      <link>https://dev.to/coppersundev/idor-and-access-control-in-ai-apis-by-the-numbers-3je5</link>
      <guid>https://dev.to/coppersundev/idor-and-access-control-in-ai-apis-by-the-numbers-3je5</guid>
      <description>&lt;p&gt;OWASP's Top 10 2021 found broken access control in 94% of tested applications — the highest occurrence count of any category, at 318,487 logged instances. The API-specific version of the same bug, Broken Object Level Authorization, ranks first in OWASP's API Security Top 10 2023 and showed up in 27% of real attack traffic against production APIs in Salt Labs' most recent report. An AI assistant asked for a REST endpoint that returns a user's record will write one without hesitation. Whether it checks that the caller owns that record is a separate question, and the data below says it usually never gets asked. This post walks through the prevalence and attack numbers, then draws the line most AI-code-security writing skips: which of these patterns a deterministic scanner can flag, and which require a human, or an AI assistant with real context, to judge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Broken Access Control Is OWASP's Most Common Finding
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats OWASP's Top 10 2021 ranking as the baseline number for how often this class of bug survives into a tested application. Broken Access Control moved from fifth place in the 2017 list to first in 2021, present in 94% of applications OWASP's contributors tested, with 318,487 total occurrences mapped across 34 separate CWEs — more raw occurrences than any other 2021 category.&lt;/p&gt;

&lt;p&gt;The average incidence rate across those 34 CWEs sits at 3.81%, but the ceiling runs far higher: the single most common CWE inside the category peaks at a 55.97% incidence rate among the applications where it was tested. OWASP's &lt;a href="https://owasp.org/Top10/A01_2021-Broken_Access_Control/" rel="noopener noreferrer"&gt;A01:2021 Broken Access Control page&lt;/a&gt; also logs 19,013 CVEs tied to the category, a volume that puts it ahead of injection, cryptographic failures, and every other 2021 category by raw occurrence count. Access control is a category, not one bug, and IDOR is one of the shapes it takes most often.&lt;/p&gt;

&lt;p&gt;The 34 CWEs mapped into A01:2021 span path traversal, forced browsing past access checks, and privilege escalation alongside the identifier-swap pattern IDOR describes. That range is part of why access control rarely shows up as a single named line item on a vulnerability report — it's a family of related gaps, and an IDOR is the member of that family an API endpoint runs into first.&lt;/p&gt;

&lt;h2&gt;
  
  
  BOLA Is the API-Specific Name for the Same Failure
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats OWASP's API Security Top 10 2023 as the sharper, API-specific lens on the same category. API1:2023 Broken Object Level Authorization ranks first among API risks, and OWASP rates the underlying weakness widespread in prevalence and easy for both an attacker to exploit and a tester to detect, once someone knows to look.&lt;/p&gt;

&lt;p&gt;OWASP's &lt;a href="https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/" rel="noopener noreferrer"&gt;API1:2023 entry&lt;/a&gt; describes the mechanics plainly: an API exposes an object identifier somewhere in the request, sequential integer, UUID, or plain string, and the server trusts that identifier without checking whether the caller may touch the object behind it. IDOR is the general term for this failure in any application, web page or API alike. BOLA is what it looks like on an API endpoint. The identifier moves from a URL path on a web form to a JSON field or query parameter, and the fix stays identical either way: compare the caller's identity against the resource's owner before the response goes out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Attacks Are Already Happening in Production Traffic
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats Salt Labs' Q1 2025 State of API Security Report as evidence that BOLA isn't a finding that only shows up in a pentest write-up. Drawn from 206 surveyed IT and security professionals plus anonymized traffic from Salt Security's own customers, the report found broken object-level authorization responsible for 27% of observed attack traffic, with BOLA and injection attacks combined behind 37% of the production API issues respondents reported.&lt;/p&gt;

&lt;p&gt;Two more numbers from the same report frame the stakes. Ninety-nine percent of respondents said they'd hit some kind of API security issue in the past 12 months, and 55% had slowed the rollout of a new application specifically over API security concerns. &lt;a href="https://salt.security/press-releases/salt-labs-state-of-api-security-report-reveals-99-of-respondents-experienced-api-security-issues-in-past-12-months" rel="noopener noreferrer"&gt;Salt Labs' report&lt;/a&gt; frames this as an attack surface growing faster than most teams' review capacity can keep up with. An AI assistant generating new endpoints every sprint adds straight to that surface. Each one is a fresh object-level check that either exists or doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI-Generated APIs Skip the Ownership Check by Default
&lt;/h2&gt;

&lt;p&gt;BrassCoders sees the same shape repeatedly across AI-generated CRUD endpoints: a handler that accepts an object ID, fetches the row, and returns it, with no comparison against the caller's identity anywhere in the function. The pattern is not a training defect. It's the natural result of a prompt that specifies what an endpoint returns without specifying who's allowed to ask for it.&lt;/p&gt;

&lt;p&gt;Ask an AI assistant for get user by id and it produces exactly that: a route, a database lookup, a response. Ownership is a fact that lives in your application's data model, not in the prompt, and an assistant that hasn't been told which callers should reach which records has no way to add the check unprompted. The result reads as complete code. It passes type-checking and a casual review, because every line does what it appears to do. The missing line, the one comparing the caller's ID against the resource owner, leaves no syntactic gap for a scanner or a reviewer to notice — which is exactly why OWASP rates the underlying weakness easy to exploit and hard to catch by accident.&lt;/p&gt;

&lt;p&gt;The gap compounds when the same assistant generates several similar endpoints in one sitting: get by id, update by id, delete by id, list by owner, each one a fresh instance of the same missing comparison. A developer writing that dozen routes by hand might carry the ownership rule forward mentally after the first one. An assistant regenerating each handler from a fresh prompt has no such continuity unless the project's authorization pattern is written down somewhere it can read.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Benchmark Like crAPI Actually Tests
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats OWASP's crAPI project as the concrete way to test whether a detection layer or a review process actually catches BOLA, instead of trusting that it would. Short for completely ridiculous API, crAPI is a deliberately vulnerable car-marketplace application, built as a multi-service stack and modeled directly on the OWASP API Security Top 10, with BOLA scenarios included by design.&lt;/p&gt;

&lt;p&gt;The project carries more than 1,600 stars on &lt;a href="https://github.com/OWASP/crAPI" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and ships a documented set of challenges rather than one bug to find. Running a scanner, an AI code-review session, or a manual test plan against crAPI answers a narrower and more useful question than whether it catches IDOR in general: whether it catches this specific object-level authorization gap, in this specific vulnerable app, where the answer is already known. Builders who want a sanity check on their own detection stack have somewhere to point it before trusting that stack against a real API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What BrassCoders Can and Cannot Flag
&lt;/h2&gt;

&lt;p&gt;BrassCoders does not detect IDOR or BOLA directly, because confirming an ownership check is correct requires knowing the application's authorization rules, and that context lives outside any single file a scanner reads. What BrassCoders' 12 bundled scanners do flag: routes with no visible auth decorator where the framework makes one detectable, mass-assignment-shaped update calls that write every request field to a database row, and hardcoded credentials sitting in the same handler.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://semgrep.dev/" rel="noopener noreferrer"&gt;Semgrep&lt;/a&gt;, one of the scanners inside BrassCoders' OSS core, is the pattern-matching engine behind that structural layer. It can match the shape of a missing decorator or a suspicious field-mapping loop, but it cannot evaluate whether the comparison inside an existing check uses the right field. That evaluation is a judgment about your data model, and BrassCoders hands it to the AI assistant reading its YAML output, or to a human reviewer, rather than guessing at it. Reporting the structural pattern honestly, without pretending to have verified the logic behind it, is the design choice. A wrong demotion here would be worse than an unflagged gap, because it would tell the AI triage layer the finding was already checked when it wasn't.&lt;/p&gt;

&lt;p&gt;The same honesty applies in the other direction. A scanner that guessed right most of the time and stayed silent the rest would train an AI triage layer to trust that silence, and the one case where the guess failed is the one that ships. BrassCoders' scanners report what they can verify structurally and nothing more. That's a narrower claim than detecting IDOR outright, but a more honest one — and it's the claim an AI assistant reading the YAML output can actually build on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Catches an IDOR Before It Ships
&lt;/h2&gt;

&lt;p&gt;BrassCoders points builders at the same fix OWASP's own testing guidance recommends: write a test that requests every object-returning or object-modifying endpoint as a user who shouldn't have access, and assert a rejection instead of a 200. That test doesn't require a scanner at all. It requires enumerating who owns what, which is exactly the step an AI assistant skips when nobody tells it the answer.&lt;/p&gt;

&lt;p&gt;Threat modeling before the code exists, authorization tests that run in CI, and DAST or manual testing against the live app are the three controls OWASP names as the ones that actually reason about intent instead of code shape. None of them are exotic. All three require someone to write down who should reach which resource — a step that's easy to skip under a deadline and impossible for a pattern scanner to reconstruct after the fact.&lt;/p&gt;

&lt;p&gt;Practically, that means an authorization test suite grows in step with the endpoint list rather than as an afterthought scheduled for later. Every new object-returning route gets a companion test asserting what happens when someone who doesn't own the object asks for it anyway. That test is cheap to write once the ownership rule is known, and it catches the exact class of bug the numbers above describe before it reaches a review, a scanner, or an attacker.&lt;/p&gt;




&lt;p&gt;BrassCoders runs on macOS, Linux, and Windows (WSL2). Install with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;brasscoders
brasscoders scan /path/to/your/api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OSS core is Apache 2.0 and free. &lt;a href="https://coppersun.dev/pricing/" rel="noopener noreferrer"&gt;BrassCoders Paid&lt;/a&gt; adds AI-powered enrichment for $12/dev/month — 50M tokens included, cancel any time via &lt;code&gt;brasscoders portal&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>benchmarking</category>
    </item>
    <item>
      <title>The CVE Record on Insecure Deserialization in AI Python Code</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Fri, 04 Sep 2026 15:57:10 +0000</pubDate>
      <link>https://dev.to/coppersundev/the-cve-record-on-insecure-deserialization-in-ai-python-code-11eg</link>
      <guid>https://dev.to/coppersundev/the-cve-record-on-insecure-deserialization-in-ai-python-code-11eg</guid>
      <description>&lt;p&gt;PyYAML's &lt;code&gt;yaml.load&lt;/code&gt; carried a CVE rated 9.8 out of 10. PyTorch's &lt;code&gt;torch.load&lt;/code&gt; carried one rated 9.3, in 2025, inside a parameter that PyTorch's own documentation called the safe way to load a model. Insecure deserialization, tracked by MITRE as &lt;a href="https://cwe.mitre.org/data/definitions/502.html" rel="noopener noreferrer"&gt;CWE-502&lt;/a&gt;, is not a hypothetical risk that AI code generation might someday introduce. It has a CVE history stretching back a decade, and the pattern keeps resurfacing one abstraction layer deeper each time a new ML framework ships its own version of "just load the file."&lt;/p&gt;

&lt;p&gt;This matters for anyone reviewing AI-generated Python because the failure mode is invisible at the call site. &lt;code&gt;yaml.load(f)&lt;/code&gt; and &lt;code&gt;yaml.safe_load(f)&lt;/code&gt; look identical in a diff — one word apart, wildly different security posture. &lt;code&gt;torch.load(path, weights_only=True)&lt;/code&gt; reads like a safety flag was already applied. An AI assistant completing a config loader or a model-loading function has no reason to know which of these APIs carries a decade of CVE history behind it; the prompt asked for functionality, and both options satisfy it on safe input.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bug That Won't Die: PyYAML's yaml.load
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats CVE-2017-18342 as the reference case for why &lt;code&gt;yaml.load&lt;/code&gt; is unsafe by default. PyYAML versions before 5.1 let &lt;code&gt;yaml.load&lt;/code&gt; execute arbitrary code on crafted input — no authentication, no user interaction, exploitable over the network. The advisory rates it CVSS 9.8, near the ceiling of the scale.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/advisories/GHSA-rprw-h62v-c2w7" rel="noopener noreferrer"&gt;GitHub Security Advisory&lt;/a&gt; explains the mechanism, and it's straightforward once you see it. PyYAML's full &lt;code&gt;Loader&lt;/code&gt; supports Python-specific tags like &lt;code&gt;!!python/object/apply:subprocess.run&lt;/code&gt;, which construct and immediately call arbitrary Python objects during parsing. A YAML file is not just data to this loader; it's a set of instructions for building objects, and one of those objects can be a running shell command. PyYAML's fix, shipped in version 5.1 after an earlier attempt in 4.1 got rolled back for breaking compatibility, changed the library's default &lt;code&gt;Loader&lt;/code&gt; and pushed developers toward &lt;code&gt;yaml.safe_load&lt;/code&gt;, which restricts parsing to plain Python types with no object construction. Eight years after the CVE, &lt;code&gt;yaml.load&lt;/code&gt; with the unsafe &lt;code&gt;Loader&lt;/code&gt; still shows up in freshly generated code, because the API itself never went away — only the recommendation changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Same Bug, Three Layers Deeper: PyTorch's torch.load
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats CVE-2025-32434 as proof that a documented safe mode can still ship a CWE-502 hole. PyTorch's own documentation recommended &lt;code&gt;torch.load(weights_only=True)&lt;/code&gt; as the way to load an untrusted checkpoint without risk. Rated CVSS 9.3, the vulnerability showed that flag alone did not stop remote code execution on PyTorch 2.5.1 and earlier.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/pytorch/pytorch/security/advisories/GHSA-53q9-r3pm-6pq6" rel="noopener noreferrer"&gt;security advisory&lt;/a&gt; is worth sitting with for a moment. This wasn't a case of a developer skipping a security flag out of ignorance. This was the flag PyTorch told everyone to use, in code that followed the documentation exactly, still carrying a critical vulnerability. The fix landed in PyTorch 2.6.0 — a version bump, not a code change on the caller's side. A model-loading function generated by an AI assistant that correctly sets &lt;code&gt;weights_only=True&lt;/code&gt; looks like defensive code. It is defensive code, against everything except the specific gap this CVE closed, on every PyTorch install before 2.6.0. Version pinning matters here in a way that a code reviewer scanning for the presence of a safety flag would miss entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Even The Framework's Convenience Wrapper Isn't Safe
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats CVE-2026-12484 as evidence the deserialization risk moved into the ML framework's own convenience wrappers, not just raw pickle calls. Keras's &lt;code&gt;TorchModuleWrapper.from_config&lt;/code&gt; method calls &lt;code&gt;torch.load&lt;/code&gt; with &lt;code&gt;weights_only=False&lt;/code&gt; by default, outside an explicit safe-mode context. Rated CVSS 7.8, it was patched in Keras 3.12.3 and 3.15.0.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/advisories/GHSA-v2w2-w228-c444" rel="noopener noreferrer"&gt;full advisory&lt;/a&gt; shows what's absent from that vulnerable code path: the word "pickle." Nobody writing or reviewing a call to &lt;code&gt;TorchModuleWrapper.from_config&lt;/code&gt; sees a deserialization primitive at all. They see a Keras layer-loading API, three abstraction layers removed from &lt;code&gt;pickle.load&lt;/code&gt;, which is itself the primitive &lt;code&gt;torch.load&lt;/code&gt; wraps. This is the pattern that makes AI-generated ML code specifically risky compared to AI-generated general-purpose Python: the unsafe call gets buried inside framework glue that reads as routine, safe-looking model plumbing. A reviewer — human or AI — pattern-matching on the literal string &lt;code&gt;pickle&lt;/code&gt; will miss every one of these.&lt;/p&gt;

&lt;h2&gt;
  
  
  Malicious Models Are Already In The Wild
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats JFrog's Hugging Face research as the canonical evidence that pickle-based model deserialization is an active exploitation vector, not a theoretical one. JFrog's security research team found close to 100 malicious models on Hugging Face carrying genuine harmful payloads, with PyTorch pickle files the most common carrier. One model, uploaded by an account the researchers named, opened a reverse shell to an attacker-controlled IP address the moment it was loaded.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://jfrog.com/blog/data-scientists-targeted-by-malicious-hugging-face-ml-models-with-silent-backdoor/" rel="noopener noreferrer"&gt;full writeup&lt;/a&gt; has a detail worth sitting with too. This wasn't a synthetic test model or a proof-of-concept the researchers built themselves. It was a real file, uploaded to a real public repository, that a real data scientist could have downloaded and loaded into a training pipeline with a single &lt;code&gt;torch.load&lt;/code&gt; call. An AI assistant asked to "write a script that downloads and loads the latest checkpoint from this Hugging Face repo" will produce exactly the call that triggers a payload like this one, because nothing in that prompt distinguishes a trusted checkpoint from a hostile one. The model file itself is the untrusted input, and it looks identical to a legitimate one until it's already running.&lt;/p&gt;

&lt;h2&gt;
  
  
  What The Standard Library Already Warned You About
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats the pickle module's own documentation as the most unimpeachable source in this category — the standard library warns about itself. The documentation states plainly: "The pickle module is not secure. Only unpickle data you trust." It goes further, pointing to &lt;code&gt;json&lt;/code&gt; for untrusted data or an &lt;code&gt;hmac&lt;/code&gt; signature to verify data hasn't been tampered with.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.python.org/3/library/pickle.html" rel="noopener noreferrer"&gt;pickle module docs&lt;/a&gt; carry that warning; the &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP Deserialization Cheat Sheet&lt;/a&gt; names the same Python danger patterns explicitly — &lt;code&gt;pickle&lt;/code&gt;, &lt;code&gt;c_pickle&lt;/code&gt;, and PyYAML's &lt;code&gt;load&lt;/code&gt; method — and adds two general defenses that apply beyond Python: prefer a plain data format that can't encode executable objects, or sign serialized data and reject anything unsigned before deserializing it. Neither source is buried in an obscure security blog. One ships inside the interpreter every Python developer already has installed. When an AI assistant writes &lt;code&gt;pickle.load(untrusted_stream)&lt;/code&gt;, it isn't missing an edge case the industry hasn't documented. It's contradicting a warning that ships in the same standard-library page it presumably drew the function signature from.&lt;/p&gt;

&lt;h2&gt;
  
  
  What BrassCoders Catches — And What It Doesn't
&lt;/h2&gt;

&lt;p&gt;BrassCoders' Bandit integration flags &lt;code&gt;pickle.load&lt;/code&gt;, &lt;code&gt;pickle.loads&lt;/code&gt;, and &lt;code&gt;cPickle&lt;/code&gt; calls under rule B301, and unsafe &lt;code&gt;yaml.load&lt;/code&gt; calls under rule B506, structurally, on every scan. The detection is pattern-based: any call matching the signature gets flagged, full stop, regardless of whether the surrounding code happens to be safe in this particular instance.&lt;/p&gt;

&lt;p&gt;That's a deliberate design choice, not a limitation BrassCoders is trying to hide. Deciding whether a specific &lt;code&gt;pickle.load&lt;/code&gt; call actually receives untrusted input requires reading the surrounding code: where the bytes came from, whether they crossed a network boundary, whether a user or an external file supplied them. That's context inference, and BrassCoders doesn't do context inference. It reports the pattern honestly and leaves the "is this one real" judgment to whichever AI assistant reads the finding next, the same division of labor that governs every scanner BrassCoders bundles.&lt;/p&gt;

&lt;p&gt;The model-file side of the problem is worth pairing with source-level scanning. &lt;a href="https://github.com/protectai/modelscan" rel="noopener noreferrer"&gt;ModelScan&lt;/a&gt;, from Protect AI, reads Pickle, SavedModel, and H5 model files byte-by-byte to flag unsafe code signatures without executing them — coverage for the file someone downloaded, complementary to BrassCoders' coverage of the call site that loads it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ML-Specific Fix: Stop Deserializing Executable Code At All
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats the shift from pickle-based checkpoints to a byte-buffer format as the closest thing this category has to a structural fix rather than a patched flag. Hugging Face's safetensors format stores tensors as raw byte buffers with a JSON header describing shape and dtype — no Python object graph, no &lt;code&gt;__reduce__&lt;/code&gt; method, nothing to execute. Loading a safetensors file cannot run code, by construction, the same guarantee &lt;code&gt;yaml.safe_load&lt;/code&gt; gives you for YAML and &lt;code&gt;json.loads&lt;/code&gt; gives you for arbitrary Python objects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/huggingface/safetensors" rel="noopener noreferrer"&gt;Safetensors&lt;/a&gt; is a different kind of fix than the four CVEs above required. PyYAML's, PyTorch's, and Keras's fixes each closed one specific hole in a format designed to deserialize executable objects — the next hole in the same design is only a matter of time, which is exactly what happened three times in a row. Safetensors sidesteps the whole design. An AI assistant asked to "load a model checkpoint" has no strong reason to prefer one format over the other unless the prompt or the surrounding codebase steers it there; a &lt;code&gt;requirements.txt&lt;/code&gt; pinned to a current framework version and a preference for &lt;code&gt;.safetensors&lt;/code&gt; over &lt;code&gt;.bin&lt;/code&gt; or &lt;code&gt;.pt&lt;/code&gt; checkpoint files where the model publisher offers both closes more risk than either alone. Many popular Hugging Face repositories now publish both formats side by side. The safer one is often already sitting there, just not the default the older tutorial used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce The Pattern Yourself
&lt;/h2&gt;

&lt;p&gt;None of the four CVE-class findings above require special access to reproduce. &lt;code&gt;pip install brasscoders&lt;/code&gt;, point it at any Python project with a &lt;code&gt;pickle.load&lt;/code&gt;, &lt;code&gt;pickle.loads&lt;/code&gt;, or bare &lt;code&gt;yaml.load&lt;/code&gt; call, and the B301 or B506 finding shows up in the scan output with file, line, and severity. Run it against a project that loads Hugging Face checkpoints and check whether the loading code pins a framework version alongside the safety flag — the version is the part a quick read-through tends to skip.&lt;/p&gt;

&lt;p&gt;The full &lt;a href="https://coppersun.dev/research/insecure-deserialization/" rel="noopener noreferrer"&gt;research index entry&lt;/a&gt; for this category has the complete CVE list, the primary-source advisories, and the OWASP remediation reference, kept current as new advisories land. The OSS core is free and Apache 2.0 licensed; BrassCoders Paid adds AI-powered enrichment on top at $12/dev/month.&lt;/p&gt;

</description>
      <category>security</category>
      <category>engineering</category>
    </item>
    <item>
      <title>AI Code License Risk From Training-Data Memorization</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Fri, 04 Sep 2026 15:55:37 +0000</pubDate>
      <link>https://dev.to/coppersundev/ai-code-license-risk-from-training-data-memorization-2cij</link>
      <guid>https://dev.to/coppersundev/ai-code-license-risk-from-training-data-memorization-2cij</guid>
      <description>&lt;p&gt;Your AI coding assistant did not write that function from scratch. It predicted the next token, and the token before that, from a model trained on a large slice of public code. Most of the time the result is original enough that nobody thinks twice. Sometimes it is not — the model reproduces a chunk of training data closely enough that the code carries the license of wherever it came from, and nobody in the room knows it.&lt;/p&gt;

&lt;p&gt;That gap has a name now: code provenance risk. It is not hypothetical. GitHub has published its own numbers on how often it happens with Copilot. A federal lawsuit over exactly this question has been in litigation since 2022 and is still unresolved. And the underlying mechanism — why some code gets memorized and reproduced while most doesn't — has been measured directly by researchers, not guessed at.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Language Models Memorize Training Data At All
&lt;/h2&gt;

&lt;p&gt;BrassCoders points builders to Carlini et al.'s 2022 ICLR paper, "Quantifying Memorization Across Neural Language Models," as the foundational evidence that language models memorize exact snippets rather than only learning generalized patterns. The paper measured three separate drivers of memorization, each with a log-linear relationship to how much verbatim text a model can be made to emit: model capacity, how many times a given example appeared in the training set, and how many tokens of context the model is prompted with.&lt;/p&gt;

&lt;p&gt;The duplication finding is the one that matters for license risk. A snippet that shows up once in an obscure repository is unlikely to be memorized. A snippet that shows up thousands of times across public GitHub — a common utility function, a standard boilerplate block, a widely-copied algorithm implementation — is exactly the kind of example a model is statistically most likely to have memorized. And code that gets duplicated that often across public repositories tends to carry a specific, traceable license, because that's how it propagated in the first place.&lt;/p&gt;

&lt;p&gt;This is not a claim that most AI-generated code is memorized. Carlini and coauthors were studying the general phenomenon, not code specifically, and the paper predates the current generation of code-focused models by several years. What it establishes is the mechanism: duplication in training data predicts reproduction in output. Any coding assistant trained on public repositories inherits this property to some degree.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Often This Actually Happens, By GitHub's Own Numbers
&lt;/h2&gt;

&lt;p&gt;BrassCoders treats GitHub's own published data as the most concrete evidence available, because it comes from the vendor measuring its own product rather than a third party estimating from outside. GitHub reports that matches to public code occur in under 1% of Copilot suggestions, using a duplication-detection filter that checks each suggestion's surrounding 150 characters, roughly 65 lexemes, against an index of public code on GitHub.com. The check runs in a 10-20 millisecond budget so it doesn't slow the editor down. When code referencing finds a match, GitHub surfaces the source repositories and their licenses so a developer can decide whether to keep the suggestion, add attribution, or discard it.&lt;/p&gt;

&lt;p&gt;Under 1% sounds small. Read the fine print, though: GitHub's own documentation notes matches are far more frequent in empty or nearly empty files than in files with existing surrounding code — the model has less context to steer it toward something novel, so it leans harder on what it has seen before. And a sub-1% per-suggestion rate is not a sub-1% per-year rate for a team. A developer using a coding assistant heavily can accept dozens of suggestions a day. Multiply that across a team, across a year, and a rare event stops being rare in absolute terms.&lt;/p&gt;

&lt;p&gt;It's worth being precise about what this number does and doesn't tell you. It's one vendor's own filtered measurement of its own product's suggestions, using its own detection threshold. It says nothing about assistants that don't run an equivalent filter, and it says nothing about code generated in longer sessions where more context accumulates. Treat it as a floor on the true rate, not a ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Legal Fight Nobody Has Resolved Yet
&lt;/h2&gt;

&lt;p&gt;BrassCoders tracks Doe v. GitHub, Inc. as the clearest evidence that this question hasn't been settled by anyone with the authority to settle it: a court, a regulator, or a standards body. Filed in the Northern District of California in November 2022 (No. 22-cv-06823-JST), the suit alleges that Copilot reproduces licensed open-source code without the attribution its licenses require, naming GitHub, Microsoft, and OpenAI as defendants. As of 2026 the case is still active, on appeal, unresolved.&lt;/p&gt;

&lt;p&gt;According to case commentary published by &lt;a href="https://lawreview.syr.edu/update-in-copilot-copyright-claim-may-affect-future-challenges-of-artificial-intelligence/" rel="noopener noreferrer"&gt;Syracuse Law Review&lt;/a&gt;, the district court dismissed most of the original claims in 2024. The core dismissed claim rested on a DMCA provision protecting copyright management information; the court read that provision as requiring the AI's output to be an identical copy of the original work rather than a modification of it, and found Copilot's output didn't meet that bar. The plaintiffs appealed to the Ninth Circuit later that year, arguing the identicality reading is wrong.&lt;/p&gt;

&lt;p&gt;Don't read either side of that ruling as a final answer. A dismissal on a narrow statutory reading isn't a finding that AI-generated code is legally safe, any more than a pending appeal is evidence that it isn't. What the case does establish, plainly, is that the underlying question — does license law's traditional identical-copy standard even fit a system that produces near-verbatim, modified output — hasn't been answered by any appellate court yet. Builders shipping AI-generated code into a product with real IP exposure are operating in that gap today, not after the case resolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Provenance Scanning Actually Checks
&lt;/h2&gt;

&lt;p&gt;BrassCoders points to ScanCode Toolkit, maintained by nexB and the AboutCode project, as a canonical example of what a purpose-built provenance scanner does differently from a security or quality scanner. It walks a codebase looking for license text, copyright notices, and package origin metadata, then emits the result as a structured inventory — JSON natively, or in the SPDX and CycloneDX formats that a software bill of materials (SBOM) uses. That's a different question than "does this code have a SQL injection bug." A security scanner reads code for dangerous patterns. A provenance scanner reads code for where it came from.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.cisa.gov/sbom" rel="noopener noreferrer"&gt;Cybersecurity and Infrastructure Security Agency&lt;/a&gt; frames the compliance side of the same problem. CISA defines an SBOM as a nested inventory, a list of ingredients that make up a software component, and treats it as a building block for supply-chain risk management across government and industry. The agency, working with international partners, publishes minimum-elements guidance that sets the baseline fields a compliant SBOM has to record. For a team shipping into a regulated industry or a government contract, an SBOM built by actually scanning the shipped code is the artifact that turns "we think our AI-generated code is fine" into something an auditor can check.&lt;/p&gt;

&lt;p&gt;Neither of these tools tells you whether a specific line of AI-generated code was memorized from a specific training example. Nobody can answer that with certainty from the output alone; the training data isn't public for most commercial models. What provenance scanning gives you is the next best thing: a record of what license terms and copyright notices are detectable in what you actually shipped, checked systematically instead of by whichever engineer happened to notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where BrassCoders Fits In This Picture
&lt;/h2&gt;

&lt;p&gt;BrassCoders runs 12 scanners against your codebase — Bandit, Pylint, Pyre/Pysa, Semgrep, ast-grep, detect-secrets, plus custom detectors for secrets, privacy, AI-specific patterns, performance, content moderation, and JavaScript/TypeScript — and every one of them answers a question about what the code does, not where it came from. License and provenance scanning is a fundamentally different detection problem: it's a matching problem against a corpus of known licensed text, not a pattern-matching problem against known-bad code shapes. BrassCoders doesn't attempt it, and this post isn't an argument that it secretly does.&lt;/p&gt;

&lt;p&gt;The honest framing matters more than the feature gap. BrassCoders was built as a dumb-but-honest pattern reporter, deliberately: it reports what a deterministic scanner can verify, and leaves the context-dependent judgment calls to whatever AI assistant is reading its output. License provenance is exactly the kind of judgment call that doesn't fit a pattern reporter — whether a 12-line utility function is common enough to be unprotectable, or specific enough to carry real license weight, requires comparing against a corpus BrassCoders was never built to hold. That's ScanCode Toolkit's job, or a commercial SBOM tool's job, run as a separate pass alongside whatever security and quality scanning you already do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What To Do About It Today
&lt;/h2&gt;

&lt;p&gt;Start with the parts of this that are actually actionable now, before the litigation resolves and before any standards body issues a definitive rule. Run a provenance scanner like ScanCode Toolkit on your codebase periodically, the same way you'd run a dependency audit — it's a separate pass, not a replacement for anything you already run. Treat any AI-generated block that looks unusually idiomatic or complete for a first draft as worth a second look; that's often a sign the model had strong context to draw from, which correlates with memorization. Keep a record of what you find, even a simple one; an SBOM doesn't need to be sophisticated to be useful, it needs to exist.&lt;/p&gt;

&lt;p&gt;None of this eliminates the risk. It converts an unknown into something you can reason about, which is the most any team can do with a legal question that's still being argued in front of a federal appeals court.&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>engineering</category>
    </item>
    <item>
      <title>Why AI Transcription Is Affordable Now</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Wed, 02 Sep 2026 16:00:04 +0000</pubDate>
      <link>https://dev.to/coppersundev/why-ai-transcription-is-affordable-now-31p8</link>
      <guid>https://dev.to/coppersundev/why-ai-transcription-is-affordable-now-31p8</guid>
      <description>&lt;p&gt;&lt;strong&gt;BrassTranscripts&lt;/strong&gt; can price accurate transcription at a few dollars per file because a research breakthrough called self-supervised learning removed the single most expensive ingredient in speech recognition: enormous hand-labeled datasets. Once AI could learn the structure of speech from unlabeled audio and fine-tune on only a little transcribed speech, high accuracy stopped being something you had to pay a premium for — it became the default.&lt;/p&gt;

&lt;p&gt;For years, the reason good transcription was expensive had nothing to do with the software running on your file. It was the cost, buried upstream, of paying people to transcribe thousands of hours of audio by hand just to teach the model what words sound like. This post explains how that bottleneck disappeared, why accuracy and affordability now come together instead of trading off, and what that means for the price you pay per file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Navigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The labeled-data bottleneck that made transcription expensive&lt;/li&gt;
&lt;li&gt;What self-supervised learning changed&lt;/li&gt;
&lt;li&gt;The wav2vec 2.0 results, in plain terms&lt;/li&gt;
&lt;li&gt;Why affordable no longer means inaccurate&lt;/li&gt;
&lt;li&gt;What actually determines your transcript quality now&lt;/li&gt;
&lt;li&gt;Frequently Asked Questions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Labeled-Data Bottleneck {#the-labeled-data-bottleneck}
&lt;/h2&gt;

&lt;p&gt;The historical cost of accurate speech recognition was human labeling, not computing: someone had to listen to thousands of hours of audio and type out every word so a model had examples to learn from. That labeling labor, not the algorithm, is what kept professional transcription priced out of reach for most people.&lt;/p&gt;

&lt;p&gt;Traditional supervised speech models learned only from paired examples — an audio clip and its verified transcript. To cover the variety of real speech (accents, vocabularies, recording conditions), you needed a very large paired dataset, and every hour of it had to be transcribed by a person first. That made the training pipeline slow, expensive, and impossible to scale cheaply, and those costs flowed straight through to what customers paid.&lt;/p&gt;

&lt;p&gt;Consider what that meant in practice. A single hour of professionally transcribed and verified audio could take several hours of skilled human labor to produce. Multiply that by the thousands of hours needed to train a model that handles many accents and topics, and the labeling budget dwarfs the compute budget. Every provider paid some version of that tax, and it showed up as high per-minute pricing, minimum commitments, and subscriptions. If you want a refresher on the vocabulary in this space, the &lt;a href="https://brasstranscripts.com/blog/ai-transcription-glossary-key-terms" rel="noopener noreferrer"&gt;AI transcription glossary of key terms&lt;/a&gt; defines labeling, fine-tuning, and word error rate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Self-Supervised Learning Changed {#what-self-supervised-learning-changed}
&lt;/h2&gt;

&lt;p&gt;Self-supervised learning let a model learn the patterns of speech from raw, unlabeled audio first, and only then fine-tune on a small amount of transcribed speech. BrassTranscripts benefits from this shift directly: the most expensive part of building an accurate model — the hand-labeling — was largely replaced by cheap, abundant unlabeled audio.&lt;/p&gt;

&lt;p&gt;The landmark demonstration is the paper "wav2vec 2.0: A Framework for Self-Supervised Learning of Speech Representations" by Alexei Baevski, Henry Zhou, Abdelrahman Mohamed, and Michael Auli at Meta AI (FAIR), published at NeurIPS 2020 (&lt;a href="https://arxiv.org/abs/2006.11477" rel="noopener noreferrer"&gt;arxiv.org/abs/2006.11477&lt;/a&gt;). Its core idea is that a model can be pre-trained to understand the structure of speech from unlabeled recordings, so that afterward it needs only a fraction of the transcribed data that older approaches demanded. That inversion — lots of unlabeled audio, a little labeled audio — is the economic hinge that made accurate transcription cheap to deliver at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wav2vec 2.0 Results {#the-wav2vec-20-results}
&lt;/h2&gt;

&lt;p&gt;wav2vec 2.0 showed both that self-supervised pre-training reaches top accuracy with the full labeled set and that it stays usable with a startlingly small amount of labeled audio. BrassTranscripts points to these numbers because they make the affordability story concrete rather than hand-wavy.&lt;/p&gt;

&lt;p&gt;Measured on the standard LibriSpeech benchmark, the model reached a word error rate of 1.8 on test-clean and 3.3 on test-other when fine-tuned on the full labeled set. More striking for the cost argument: with just ten minutes of labeled audio — combined with pre-training on 53,000 hours of unlabeled audio — it still produced a usable 4.8 word error rate on test-clean and 8.2 on test-other. In plain terms, a model taught almost entirely from unlabeled recordings, plus a sliver of transcribed speech, still transcribed clean audio well. Word error rate is simply the percentage of words the system gets wrong, so lower is better; our &lt;a href="https://brasstranscripts.com/research/transcription-accuracy" rel="noopener noreferrer"&gt;research summary on transcription accuracy&lt;/a&gt; explains how that metric is measured and why it can vary by recording.&lt;/p&gt;

&lt;h2&gt;
  
  
  Affordable No Longer Means Inaccurate {#affordable-no-longer-means-inaccurate}
&lt;/h2&gt;

&lt;p&gt;Low price and high accuracy stopped being a trade-off because the thing that fell was the training cost, not the quality bar. BrassTranscripts charges a flat per-file rate rather than gating accuracy behind a premium tier, because the underlying research made professional-grade accuracy the baseline rather than an upsell.&lt;/p&gt;

&lt;p&gt;This is where older pricing intuitions mislead people. When labeling was the dominant cost, it was reasonable to assume "cheaper transcription" meant "worse transcription." After self-supervised learning, the assumption inverts: the marginal cost of running an already-trained, highly accurate model on your file is low, so charging premium prices for accuracy alone is hard to justify.&lt;/p&gt;

&lt;p&gt;It is worth being precise about what the research does and does not claim. The wav2vec 2.0 numbers describe a specific model on a specific English read-speech benchmark, not a guarantee about any given file — a noisy phone recording of three people talking over each other is a harder problem than clean audiobook narration. But the direction is unmistakable: once a model can be built without an enormous hand-labeled corpus, the economics that forced high prices simply are not there anymore. For a fuller comparison of what different providers actually charge and why, see our guide on &lt;a href="https://brasstranscripts.com/blog/ai-transcription-services-how-to-choose-2026-guide" rel="noopener noreferrer"&gt;how to choose an AI transcription service in 2026&lt;/a&gt; and the breakdown of &lt;a href="https://brasstranscripts.com/blog/openai-whisper-api-pricing-2025-self-hosted-vs-managed" rel="noopener noreferrer"&gt;Whisper API pricing, self-hosted versus managed&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Determines Your Transcript Quality Now {#what-determines-your-transcript-quality-now}
&lt;/h2&gt;

&lt;p&gt;With the model itself already strong, the biggest remaining lever on accuracy is your audio: clear recording, low background noise, and speakers who do not talk over each other. BrassTranscripts runs the same advanced AI transcription on every file, so a cleaner recording improves your transcript far more than any "higher quality" purchase option ever could.&lt;/p&gt;

&lt;p&gt;That is genuinely good news for your budget. Instead of paying for tiers, you invest a few minutes in a better recording — a decent microphone, a quiet room, one speaker at a time — and let the model do the rest. Our deeper explainer on &lt;a href="https://brasstranscripts.com/blog/what-determines-transcription-accuracy" rel="noopener noreferrer"&gt;what determines transcription accuracy&lt;/a&gt; walks through the specific, controllable factors that move the number, most of which cost nothing to fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why did AI transcription become cheaper?
&lt;/h3&gt;

&lt;p&gt;AI transcription became cheaper because self-supervised learning let models learn the structure of speech from large amounts of unlabeled audio, then fine-tune on a small set of transcribed speech. This removed the need to pay humans to hand-label thousands of hours of audio, which was historically the largest cost in building an accurate speech recognition system.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is wav2vec 2.0 and why does it matter?
&lt;/h3&gt;

&lt;p&gt;wav2vec 2.0 is a 2020 speech model from Meta AI that learns speech representations from unlabeled audio before fine-tuning on transcribed speech. It matters because it showed that high-accuracy transcription no longer required massive hand-labeled datasets, which is the economic shift that made affordable, professional-grade AI transcription possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does affordable AI transcription mean lower accuracy?
&lt;/h3&gt;

&lt;p&gt;No. Modern AI transcription is affordable because the training method changed, not because quality was reduced. Research showed models can reach strong accuracy after learning from unlabeled audio, so BrassTranscripts delivers professional-grade accuracy at flat per-file pricing rather than charging more for better results.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much does BrassTranscripts cost?
&lt;/h3&gt;

&lt;p&gt;BrassTranscripts charges $2.50 for audio files 1-15 minutes and $6.00 flat for files 16 minutes and up, at any length. Automatic speaker identification and four output formats (TXT, SRT, VTT, JSON) are included at that flat rate, with support for 99+ languages and no subscription required.&lt;/p&gt;

&lt;h3&gt;
  
  
  What most affects my transcript's accuracy today?
&lt;/h3&gt;

&lt;p&gt;Because the underlying AI models are already strong, the biggest remaining factor in transcript accuracy is your audio quality — clear recordings, minimal background noise, and non-overlapping speakers. BrassTranscripts applies the same advanced AI transcription to every file, so improving your recording does more for accuracy than paying a premium tier ever could.&lt;/p&gt;

&lt;h2&gt;
  
  
  About BrassTranscripts
&lt;/h2&gt;

&lt;p&gt;BrassTranscripts is a pay-per-file AI transcription service: $2.50 for files 1-15 minutes and $6.00 flat for files 16 minutes and up, at any length. Every transcript includes automatic speaker identification and four output formats (TXT, SRT, VTT, JSON), with support for 99+ languages and no subscription required. Upload a file, pay for that file, and download professional-grade results — the affordability comes from the research described above, not from cutting quality.&lt;/p&gt;

</description>
      <category>aitranscription</category>
      <category>transcriptionaccuracy</category>
      <category>transcribeaudiototext</category>
    </item>
    <item>
      <title>How AI Adds Punctuation to Transcripts</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Tue, 01 Sep 2026 16:47:43 +0000</pubDate>
      <link>https://dev.to/coppersundev/how-ai-adds-punctuation-to-transcripts-45k1</link>
      <guid>https://dev.to/coppersundev/how-ai-adds-punctuation-to-transcripts-45k1</guid>
      <description>&lt;p&gt;Punctuation is not something a speech recognizer produces on its own. &lt;strong&gt;BrassTranscripts starts from what speech recognition actually outputs — an unpunctuated, uncapitalized stream of words — and then a separate punctuation-restoration step adds the sentence boundaries, commas, and capitalization that turn that stream into readable text.&lt;/strong&gt; Understanding that these are two distinct steps explains why a transcript can nail every word yet still need a light formatting pass, and why punctuation quality is its own dimension of transcript quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Navigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Speech Recognition Produces a Raw Word Stream&lt;/li&gt;
&lt;li&gt;Punctuation Restoration Is a Separate Step&lt;/li&gt;
&lt;li&gt;How the Punctuation Model Works&lt;/li&gt;
&lt;li&gt;Why It Works Across Languages&lt;/li&gt;
&lt;li&gt;Why Punctuation Is Its Own Quality Dimension&lt;/li&gt;
&lt;li&gt;What This Means for Your Transcripts&lt;/li&gt;
&lt;li&gt;Frequently Asked Questions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Speech Recognition Produces a Raw Word Stream
&lt;/h2&gt;

&lt;p&gt;BrassTranscripts begins with core speech recognition, which converts audio into words but nothing more — no periods, no commas, no capital letters, just a continuous lowercase sequence of the words it heard. A recognizer's only job is to map sound to tokens, so its native output looks like "so we met on tuesday and the budget was approved but the timeline slipped" with no structure at all.&lt;/p&gt;

&lt;p&gt;That raw form is exactly what you would expect from a system trained to answer one question: what words were spoken? Nothing in that question involves where a sentence ends or whether "tuesday" should be capitalized. Those are decisions about written language, not about sound, which is why they fall outside the recognizer's job. The concepts behind these steps are covered in the &lt;a href="https://brasstranscripts.com/blog/ai-transcription-glossary-key-terms" rel="noopener noreferrer"&gt;AI transcription glossary&lt;/a&gt;, which defines the vocabulary of the transcription pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Punctuation Restoration Is a Separate Step
&lt;/h2&gt;

&lt;p&gt;The readability of a BrassTranscripts transcript comes from punctuation restoration, a dedicated post-processing step that runs after the words are recognized and inserts sentence boundaries, commas, question marks, and capitalization. This step takes the raw word stream as input and rewrites it into structured prose — "So we met on Tuesday, and the budget was approved, but the timeline slipped." — without changing the words themselves.&lt;/p&gt;

&lt;p&gt;Treating this as a separate stage is deliberate. The recognizer optimizes for hearing words correctly; the punctuation model optimizes for interpreting how those words group into sentences and clauses. Splitting the work lets each model specialize instead of forcing one system to do two very different jobs at once. The result is that punctuation is applied consistently regardless of how the speaker paused or ran sentences together, and it lands the same way across every &lt;a href="https://brasstranscripts.com/blog/choosing-the-right-transcript-format-txt-srt-vtt-json" rel="noopener noreferrer"&gt;output format you download — TXT, SRT, VTT, or JSON&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Punctuation Model Works
&lt;/h2&gt;

&lt;p&gt;BrassTranscripts relies on the same class of technique that transcription researchers have converged on: a Transformer-based language model fine-tuned specifically to predict punctuation from unpunctuated text. In their W-NUT 2020 paper "Punctuation Restoration using Transformer Models for High- and Low-Resource Languages," Alam, Khan, and Alam (2020) fine-tuned a Transformer language model — a pretrained encoder followed by a bidirectional LSTM — to label each position in a word stream with the punctuation that belongs there (&lt;a href="https://aclanthology.org/2020.wnut-1.18/" rel="noopener noreferrer"&gt;aclanthology.org/2020.wnut-1.18&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The intuition is that punctuation is a prediction problem over the sequence of words. For every gap between words, the model asks whether that gap should stay empty or hold a comma, a period, or a question mark, and for every word it asks whether it should be capitalized. Because the model reads context in both directions — the words before and after each position — it can tell that a rising, question-shaped clause needs a question mark or that a proper noun needs a capital letter. This is the same interpretive work a human editor does when cleaning up a rough transcript, done automatically at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It Works Across Languages
&lt;/h2&gt;

&lt;p&gt;Punctuation restoration is not an English-only trick — the approach generalizes to languages with far less training data available. Alam, Khan, and Alam (2020) evaluated their Transformer approach on both a high-resource language, English, and a low-resource one, Bangla, showing that the same architecture restores punctuation effectively even where large annotated corpora are scarce.&lt;/p&gt;

&lt;p&gt;That generality matters for anyone transcribing beyond English. BrassTranscripts applies punctuation and capitalization across 99+ languages, which is only practical because the underlying method does not depend on the enormous datasets that exist for English alone. The demand for non-English transcription is real and growing, as our &lt;a href="https://brasstranscripts.com/blog/ai-transcription-demand-by-language-2026-usage-data" rel="noopener noreferrer"&gt;language usage data&lt;/a&gt; shows, and readable output in every one of those languages depends on this step working outside English.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Punctuation Is Its Own Quality Dimension
&lt;/h2&gt;

&lt;p&gt;Word accuracy and punctuation accuracy are separate measures because they are produced by separate models solving separate problems. BrassTranscripts can transcribe every word in a sentence correctly while the punctuation model still places a comma in an unusual spot or splits one long spoken sentence into two — the word-error rate and the punctuation quality move independently.&lt;/p&gt;

&lt;p&gt;This is why it is a mistake to judge an entire transcript by a single number. A transcript that is flawless word-for-word may still read slightly oddly if punctuation lands imperfectly, and a light formatting cleanup fixes that without touching the words. Our deep dive on &lt;a href="https://brasstranscripts.com/blog/what-determines-transcription-accuracy" rel="noopener noreferrer"&gt;what determines transcription accuracy&lt;/a&gt; treats word recognition and formatting as distinct factors for exactly this reason, and our &lt;a href="https://brasstranscripts.com/research/transcription-accuracy" rel="noopener noreferrer"&gt;research page on transcription accuracy&lt;/a&gt; documents the measurable metrics we report. When you know punctuation is its own layer, you know where to look when something reads awkwardly — and that it is usually a two-minute edit, not a re-transcription.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for Your Transcripts
&lt;/h2&gt;

&lt;p&gt;For practical purposes, BrassTranscripts delivers transcripts with punctuation and capitalization already applied, so you receive readable sentences rather than a raw token stream. You never have to run the punctuation step yourself — it happens automatically before the file reaches you, in every format.&lt;/p&gt;

&lt;p&gt;Knowing the pipeline still helps you work faster. If a sentence break lands in an unexpected place, that is the punctuation layer, not a word error, and you can fix it in seconds. If you want to reshape the text further — say, toward clean or intelligent verbatim — you are editing formatting on top of accurate words, which is exactly the workflow described in our guide to &lt;a href="https://brasstranscripts.com/blog/verbatim-vs-clean-vs-intelligent-verbatim-transcription" rel="noopener noreferrer"&gt;verbatim, clean, and intelligent verbatim styles&lt;/a&gt;. The words are the hard part, and the machine has already done them; punctuation is the readable finish on top.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does speech recognition add punctuation automatically?
&lt;/h3&gt;

&lt;p&gt;Core speech recognition does not add punctuation — it converts audio into a raw, lowercase stream of words with no periods, commas, or capital letters. Punctuation and capitalization come from a separate post-processing step, called punctuation restoration, that runs after the words are recognized. BrassTranscripts applies this step automatically so the transcript you download reads in proper sentences.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is punctuation restoration?
&lt;/h3&gt;

&lt;p&gt;Punctuation restoration is a dedicated AI step that reads an unpunctuated word stream and predicts where sentences end, where commas and question marks belong, and which words should be capitalized. It is a distinct task from recognizing the words themselves, which is why a transcript can be accurate word-for-word yet still need light formatting cleanup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does a transcript get the words right but the punctuation wrong?
&lt;/h3&gt;

&lt;p&gt;Word accuracy and punctuation are produced by two different models solving two different problems, so they can succeed or fail independently. The speech recognizer can transcribe every word correctly while the punctuation model still places a comma awkwardly or splits a sentence, because punctuation depends on interpreting meaning and pauses rather than just identifying sounds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does punctuation restoration work in languages other than English?
&lt;/h3&gt;

&lt;p&gt;Yes — punctuation restoration generalizes across languages, including low-resource ones. Research by Alam, Khan, and Alam (2020) demonstrated the approach on both English and Bangla, and BrassTranscripts applies punctuation and capitalization across the 99+ languages it supports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I get a transcript without any punctuation applied?
&lt;/h3&gt;

&lt;p&gt;BrassTranscripts delivers transcripts with punctuation and capitalization already applied across TXT, SRT, VTT, and JSON formats, because a punctuated transcript is far more usable for reading, editing, and captioning. If you need the raw token stream for a specialized workflow, the JSON output gives you word-level data you can reformat however you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  About BrassTranscripts
&lt;/h2&gt;

&lt;p&gt;BrassTranscripts is a pay-per-file AI transcription service — no subscription, no commitment. Pricing is simple: $2.50 for files 1–15 minutes long, and a $6.00 flat rate for files 16 minutes and up, at any length. Every transcript includes automatic speaker identification and arrives in TXT, SRT, VTT, and JSON formats with punctuation and capitalization already applied, across 99+ supported languages. Upload a file, and you get back readable, structured text ready to use.&lt;/p&gt;

</description>
      <category>transcriptformats</category>
      <category>transcriptionaccuracy</category>
      <category>aitranscription</category>
    </item>
    <item>
      <title>How Speech Quality Is Measured for Transcription</title>
      <dc:creator>CopperSunDev</dc:creator>
      <pubDate>Tue, 01 Sep 2026 16:47:29 +0000</pubDate>
      <link>https://dev.to/coppersundev/how-speech-quality-is-measured-for-transcription-1nkb</link>
      <guid>https://dev.to/coppersundev/how-speech-quality-is-measured-for-transcription-1nkb</guid>
      <description>&lt;p&gt;You cannot fix what you cannot measure, and for a long time "good audio" was a matter of opinion. BrassTranscripts starts from a more useful fact: recording quality can be scored before transcription, and that score predicts how accurate the transcript will be. The audio you upload already carries measurable signals of noise, distortion, and dropouts, and each of those signals maps to a specific way a transcript can go wrong.&lt;/p&gt;

&lt;p&gt;This post explains how speech quality is actually measured in the field, what the numbers mean, and why a quality score is really an accuracy forecast in disguise. BrassTranscripts maintains a &lt;a href="https://brasstranscripts.com/research" rel="noopener noreferrer"&gt;Curated Authority Index&lt;/a&gt; of the primary research behind AI transcription, including the &lt;a href="https://brasstranscripts.com/research/audio-quality" rel="noopener noreferrer"&gt;audio quality research&lt;/a&gt; this article draws on, so you can check every claim at the source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Navigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Quality Can Be Scored Before You Transcribe&lt;/li&gt;
&lt;li&gt;What a Mean Opinion Score Actually Measures&lt;/li&gt;
&lt;li&gt;The Four Dimensions of Speech Quality&lt;/li&gt;
&lt;li&gt;Why Quality Predicts Transcript Accuracy&lt;/li&gt;
&lt;li&gt;How the Measurement Handles Real-World Audio&lt;/li&gt;
&lt;li&gt;How to Use Quality Measurement Before You Upload&lt;/li&gt;
&lt;li&gt;Frequently Asked Questions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quality Can Be Scored Before You Transcribe
&lt;/h2&gt;

&lt;p&gt;BrassTranscripts treats audio quality as a measurable property of the recording, not a subjective impression formed after reading a bad transcript. Modern speech-quality models are non-intrusive, meaning they estimate perceived quality from the degraded recording alone, without needing a pristine reference copy to compare it against.&lt;/p&gt;

&lt;p&gt;That distinction matters more than it sounds. Older quality measures required the original clean signal to measure how far a recording had drifted from it, which is useless in the real world where no clean copy of your meeting or interview exists. A non-intrusive model looks only at the file you actually have. The leading example is NISQA, described in "NISQA: A Deep CNN-Self-Attention Model for Multidimensional Speech Quality Prediction with Crowdsourced Datasets" by Gabriel Mittag, Babak Naderi, Assmaa Chehadi, and Sebastian Möller of TU Berlin (2021), published at &lt;a href="https://arxiv.org/abs/2104.09494" rel="noopener noreferrer"&gt;arxiv.org/abs/2104.09494&lt;/a&gt;. Because it needs no reference, a score like this can be produced for any recording before it is ever transcribed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Mean Opinion Score Actually Measures
&lt;/h2&gt;

&lt;p&gt;A Mean Opinion Score, or MOS, is a single number that captures overall perceived speech quality, and BrassTranscripts uses it as shorthand for how clear a recording sounds to a listener. Historically the MOS was gathered by asking human listeners to rate audio on a simple scale and averaging their responses; the achievement of modern models is predicting that human rating automatically.&lt;/p&gt;

&lt;p&gt;The overall MOS is a useful summary, but a summary hides detail. A recording that scores poorly could be too quiet, too noisy, distorted, or full of dropouts, and the single number treats all of those as the same generic "bad." NISQA was built specifically to reject that oversimplification. Alongside the overall MOS, it predicts four separate quality dimensions, so the score tells you not just that a recording is degraded but in which way it is degraded. That is the difference between a warning light and a diagnosis.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Dimensions of Speech Quality
&lt;/h2&gt;

&lt;p&gt;BrassTranscripts finds the four-dimension breakdown more actionable than any single rating, because each dimension names a defect you can actually go and fix. NISQA predicts Noisiness, Coloration, Discontinuity, and Loudness in addition to the overall MOS.&lt;/p&gt;

&lt;p&gt;Each dimension isolates one failure of the recording chain. Noisiness reflects background sound layered over the speech, the hum, chatter, or traffic competing with the voice. Coloration reflects distortion of the frequency balance, the tinny or muffled quality that comes from a poor microphone or aggressive compression. Discontinuity reflects interruptions in the signal, the gaps and dropouts that appear when audio is lost in transit. Loudness reflects level problems, speech that is too quiet or clipped from being too hot. A recording can score well on three and fail the fourth, which is exactly the information a single MOS throws away. The four-dimension model was introduced in the NISQA work by Mittag and colleagues (2021).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Quality Predicts Transcript Accuracy
&lt;/h2&gt;

&lt;p&gt;The reason to measure any of this is that input quality is the single biggest driver of transcript accuracy, and BrassTranscripts treats a low quality score as an early warning that the transcript will need scrutiny. Clean input audio consistently produces the most accurate output, a principle so well established it borders on obvious once you have compared a studio recording against a phone call of the same conversation.&lt;/p&gt;

&lt;p&gt;What the dimensional view adds is a prediction of the failure mode, not just the failure. A low Discontinuity score suggests packet-loss gaps, the kind of dropout that erases whole words and leaves the transcript missing content entirely. A low Noisiness score suggests background noise, which tends to produce misheard words rather than missing ones, as the model tries to resolve speech buried under competing sound. Coloration problems blur the acoustic distinctions between similar-sounding words, and loudness problems can push quiet speech below the threshold where it registers at all. Each dimension predicts a different way the transcript will disappoint you, which is far more useful than a vague sense that the audio was "not great." Our guide to &lt;a href="https://brasstranscripts.com/blog/what-determines-transcription-accuracy" rel="noopener noreferrer"&gt;what determines transcription accuracy&lt;/a&gt; covers why the recording matters more than the brand of software.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Measurement Handles Real-World Audio
&lt;/h2&gt;

&lt;p&gt;A quality model is only trustworthy if it was tested on the audio people actually record, and BrassTranscripts values the NISQA research precisely because it was built on real conditions rather than laboratory tone. Its corpus contains more than 14,000 speech clips spanning a wide range of distortions, including live recordings made over mobile phone, Zoom, Skype, and WhatsApp.&lt;/p&gt;

&lt;p&gt;That coverage is what makes the scores relevant to your uploads. A meeting recorded through Zoom, an interview captured on a phone, and a voice note sent over WhatsApp each degrade in characteristic ways, and a model trained on those exact channels predicts their quality reliably rather than guessing. The crowdsourced datasets behind NISQA were designed to capture this real-world variety, which is why its predictions hold up outside the lab. For a deeper look at the vocabulary around all of this, see our &lt;a href="https://brasstranscripts.com/blog/ai-transcription-glossary-key-terms" rel="noopener noreferrer"&gt;AI transcription glossary&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use Quality Measurement Before You Upload
&lt;/h2&gt;

&lt;p&gt;You do not need to run a research model to benefit from thinking in these terms, and BrassTranscripts recommends listening to your recording through the four-dimension lens before you transcribe. Ask whether the speech is buried in noise, whether it sounds distorted, whether the audio cuts in and out, and whether it is loud and clear, because those four questions map directly to the dimensions research uses to predict quality.&lt;/p&gt;

&lt;p&gt;Where you can, fix the problem the diagnosis points to rather than transcribing and hoping. If dropouts are the issue, re-export from the original source or record locally instead of over a call. If noise is the issue, move somewhere quieter or closer to the microphone. Our practical walkthroughs of &lt;a href="https://brasstranscripts.com/blog/audio-quality-secrets-perfect-transcription" rel="noopener noreferrer"&gt;audio quality secrets for perfect transcription&lt;/a&gt; and &lt;a href="https://brasstranscripts.com/blog/audio-quality-ruining-transcripts-2026-fix-guide" rel="noopener noreferrer"&gt;fixing the audio problems ruining your transcripts&lt;/a&gt; cover the specific fixes. When you are ready, the surest test is your own audio: BrassTranscripts shows a 30-word preview of every transcript before purchase, so you can confirm accuracy on the exact file you care about rather than trusting any score in the abstract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can speech quality be measured before transcription?
&lt;/h3&gt;

&lt;p&gt;Yes. Non-intrusive quality models estimate perceived speech quality from the recording alone, without needing a clean reference copy to compare against. BrassTranscripts treats this the same way a professional would: the recording carries measurable signals of clarity, noise, and dropouts that exist before a single word is transcribed, and those signals predict how the transcript will turn out.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Mean Opinion Score?
&lt;/h3&gt;

&lt;p&gt;A Mean Opinion Score, or MOS, is a single rating of overall perceived speech quality, traditionally averaged from human listeners and now predictable by models. The NISQA research goes further than one number, predicting four separate dimensions alongside the overall score. BrassTranscripts finds the multidimensional view more useful because a bad recording is rarely bad in only one way.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the four dimensions NISQA predicts?
&lt;/h3&gt;

&lt;p&gt;NISQA predicts Noisiness, Coloration, Discontinuity, and Loudness in addition to an overall MOS. Each isolates a different defect: Noisiness captures background sound, Coloration captures frequency distortion, Discontinuity captures gaps and dropouts, and Loudness captures level problems. BrassTranscripts maps these to distinct transcript failure modes, so a low score points to the specific problem to fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does a low quality score mean a bad transcript?
&lt;/h3&gt;

&lt;p&gt;Usually, and it also tells you why. A low Discontinuity score suggests packet-loss gaps that erase whole words, while a low Noisiness score suggests background noise that produces misheard words. BrassTranscripts shows a 30-word preview of every transcript before purchase, so users can confirm accuracy on their own audio rather than relying on a score alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where does call and meeting audio fit into quality measurement?
&lt;/h3&gt;

&lt;p&gt;Modern quality models are trained on real communication conditions, not just studio recordings. The NISQA corpus includes clips captured over mobile phone, Zoom, Skype, and WhatsApp, which is why its scores translate to everyday audio. BrassTranscripts sees the same patterns in practice, where the recording channel often matters more than the microphone.&lt;/p&gt;

&lt;h2&gt;
  
  
  About BrassTranscripts
&lt;/h2&gt;

&lt;p&gt;BrassTranscripts is a pay-per-file AI transcription service with no subscription. Pricing is simple: $2.50 for files 1 to 15 minutes long, and a flat $6.00 for anything 16 minutes and up, at any length. Every transcript includes automatic speaker identification and downloads in TXT, SRT, VTT, and JSON, with support for 99+ languages. Advanced AI transcription handles the recording; you just upload it. Before you pay, a 30-word preview lets you check the quality of your specific transcript, so you can confirm the result on your own audio first. When you are ready, &lt;a href="https://brasstranscripts.com" rel="noopener noreferrer"&gt;upload a file&lt;/a&gt; and see the preview yourself.&lt;/p&gt;

</description>
      <category>audioqualitytips</category>
      <category>transcriptionaccuracy</category>
      <category>aitranscription</category>
    </item>
  </channel>
</rss>
