The Comeback: From Hiatus to Security Hardening

After a long hiatus from blogging, I decided it was time to dust off my Jekyll blog and get back to writing. But when I opened my repository, I was greeted by a concerning sight: 12 GitHub Dependabot security alerts ranging from critical to low severity. These weren’t just minor updates—some were critical vulnerabilities with CVSS scores of 9.1 that could lead to memory corruption and use-after-free attacks.

This seemed like the perfect opportunity to not only get my blog back online securely, but also to try out a new approach I’d been exploring: spec-driven development using Kiro. Rather than diving straight into code changes, I decided to treat this security hardening as a proper software project with requirements, design, and implementation planning.

Why Spec-Driven Development?

Coming back to a project after months away, I realized I needed more than just quick fixes. I needed:

  • Clear documentation of what needed to be done and why
  • Systematic approach to prevent breaking changes
  • Maintainable procedures for future updates
  • Comprehensive testing strategy to ensure nothing broke

This is where Kiro’s spec-driven approach became invaluable.

The Security Landscape

The vulnerabilities spanned across multiple critical components:

  • Nokogiri (XML/HTML parser): 5 alerts including critical memory corruption issues
  • REXML (XML parser): 6 alerts mostly related to Denial of Service attacks
  • ActiveSupport (Rails component): 1 alert for potential file disclosure

Here’s what I was facing:

Critical Priority 🚨

  • CVE-2025-49794, CVE-2025-49796: Use-after-free and memory corruption in Nokogiri (CVSS 9.1)

High Priority ⚠️

  • CVE-2025-24855, CVE-2024-55549: Use-after-free in libxslt
  • CVE-2024-43398: REXML DoS vulnerability with deep XML elements

Medium Priority 📋

  • Multiple REXML DoS vulnerabilities
  • ActiveSupport file disclosure vulnerability

The Spec-Driven Approach with Kiro

Instead of randomly updating gems, I used Kiro to develop a structured methodology through proper specification:

1. Requirements Gathering

Using Kiro, I started by creating a comprehensive requirements document that captured:

  • User stories for each type of security concern
  • Acceptance criteria in EARS format (Easy Approach to Requirements Syntax)
  • Priority matrix based on vulnerability severity
  • Success metrics for the security hardening
**User Story:** As a blog maintainer, I want to analyze the current GitHub
security alerts for my gem dependencies, so that I can prioritize which
vulnerabilities need immediate attention.

#### Acceptance Criteria
1. WHEN analyzing GitHub alerts THEN the system SHALL categorize
   vulnerabilities by severity (critical, high, medium, low)
2. WHEN vulnerabilities are categorized THEN the system SHALL identify
   which gems require updates

2. Design Document

Kiro helped me create a detailed design that outlined:

  • Vulnerability analysis framework with three-phase approach
  • Gem upgrade mapping with version constraints
  • Dependency resolution strategy to prevent conflicts
  • Error handling scenarios and rollback procedures

3. Implementation Planning

The spec process generated a detailed task breakdown:

  • 8 major phases with 15 sub-tasks
  • Each task linked to specific requirements
  • Clear validation steps for each phase
  • Rollback procedures documented upfront

4. Baseline Documentation

Through the spec process, I systematically documented the current state:

  • Ruby 2.7.1 (approaching EOL)
  • Jekyll 4.1.1 (outdated)
  • Bundler 2.1.4 (incompatible with modern Ruby)
  • Multiple outdated gems with restrictive version constraints

5. Priority-Based Upgrade Strategy

The design phase helped me categorize updates by security impact and dependency relationships:

# Phase 1: Ruby runtime upgrade
Ruby 2.7.1  Ruby 3.3.9

# Phase 2: Critical security gems
nokogiri: "< 1.18.9"  "~> 1.18.0"
rexml: "< 3.3.9"  "~> 3.4.0"

# Phase 3: Major framework updates
activesupport: "< 6.1.7.5"  "~> 7.2.0"

# Phase 4: Supporting infrastructure
faraday: "< 1.0"  "~> 2.12.0"

3. Constraint Optimization

One major issue was overly restrictive version constraints that prevented security updates:

# Before: Blocking security updates
gem "faraday", "< 1.0"  # Stuck on 0.17.5 from 2019!

# After: Security-friendly constraints
gem "faraday", "~> 2.12"  # Allows patch updates

The Implementation Journey: Spec to Code

With Kiro’s comprehensive spec in hand, the implementation became straightforward. Each task was clearly defined with acceptance criteria, making it easy to know exactly what needed to be done and how to validate success.

Task-by-Task Execution

The beauty of the spec-driven approach was that I could work through each task systematically:

- [x] 1. Establish baseline and prepare for upgrades
- [x] 2. Upgrade Ruby version to latest stable
- [x] 3. Update critical security gems (Nokogiri and REXML)
- [x] 4. Update ActiveSupport to latest Rails 7.x series
- [x] 5. Update supporting gems to latest versions
- [x] 6. Optimize Gemfile version constraints for security
- [x] 7. Comprehensive testing and validation
- [x] 8. Document changes and create maintenance procedures

Ruby Version Upgrade

Following the spec’s Phase 1, the foundation needed to be solid. Ruby 2.7.1 was approaching end-of-life and causing bundler compatibility issues:

# Before
FROM jekyll/jekyll:4.1.0  # Ruby 2.7.1

# After
FROM ruby:3.3-alpine     # Ruby 3.3.9

This single change resolved multiple compatibility issues and enabled modern gem versions.

Critical Security Updates

Nokogiri: The Big One Nokogiri had the most severe vulnerabilities, including critical memory corruption issues:

# Updated from 1.16.3 to 1.18.10
gem "nokogiri", "~> 1.18"

This update resolved 5 security alerts, including the critical CVE-2025-49794 and CVE-2025-49796.

REXML: DoS Protection REXML had 6 different Denial of Service vulnerabilities:

# Updated from 3.2.5 to 3.4.4
gem "rexml", "~> 3.4"

ActiveSupport: Major Version Jump The most challenging update was ActiveSupport, requiring a major version upgrade:

# 6.0.6.1 → 7.2.2.2 (major version jump)
gem "activesupport", "~> 7.2"

Surprisingly, this major version upgrade had zero breaking changes for Jekyll usage—a testament to Rails’ commitment to backward compatibility.

Testing and Validation

Each phase included comprehensive testing:

Automated Checks

# Build verification
bundle install
jekyll build

# Functionality testing
docker-compose up -d
curl -I http://localhost:4000

Manual Verification

  • ✅ All blog posts render correctly
  • ✅ Math equations display properly (KaTeX)
  • ✅ Code syntax highlighting works
  • ✅ RSS feed generates correctly
  • ✅ SEO tags are present
  • ✅ Site navigation functions

The Results

Security Impact

  • 12 vulnerabilities resolved: From critical to low severity
  • Zero known vulnerabilities: Clean security audit
  • Future-proof constraints: Automatic security patches enabled

Performance Improvements

  • Ruby 3.3.x: Significant performance gains over 2.7.x
  • Modern HTTP/2: Faraday 2.x includes modern features
  • Updated plugins: Performance optimizations included

Version Summary

| Component | Before | After | Security Impact | |———–|——–|——-|—————–| | Ruby | 2.7.1 | 3.3.9 | Latest security patches | | Nokogiri | 1.16.3 | 1.18.10 | 5 CVEs resolved | | REXML | 3.2.5 | 3.4.4 | 6 DoS vulnerabilities fixed | | ActiveSupport | 6.0.6.1 | 7.2.2.2 | File disclosure vulnerability fixed | | Faraday | 0.17.5 | 2.12.3 | 5+ years of security updates |

The Power of Spec-Driven Development

This project reinforced why I’m excited about spec-driven development with Kiro:

Benefits I Experienced

  1. Clear roadmap: Never wondered “what should I do next?”
  2. Risk mitigation: Potential issues identified upfront in design phase
  3. Comprehensive testing: Validation criteria defined before implementation
  4. Documentation by design: Requirements and procedures created as part of the process
  5. Maintainable outcomes: Future changes have a clear framework to follow

The Kiro Advantage

  • Iterative refinement: Requirements → Design → Tasks → Implementation
  • Built-in validation: Each phase reviewed before proceeding
  • Comprehensive coverage: Nothing falls through the cracks
  • Future-ready: Maintenance procedures established from day one

Lessons Learned

1. Spec-Driven Beats Ad-Hoc

Taking time to create proper requirements and design prevented dependency conflicts and reduced risk significantly.

2. Version Constraints Matter

Overly restrictive constraints (gem "name", "< 1.0") can block critical security updates for years.

3. Major Version Upgrades Aren’t Always Breaking

ActiveSupport 6.x → 7.x had zero breaking changes for our Jekyll usage.

4. Documentation Is Critical

The spec process automatically generated detailed records of changes, rationale, and rollback procedures.

5. Kiro Makes Complex Projects Manageable

What could have been an overwhelming security crisis became a systematic, well-documented project.

What’s Next for This Blog

Now that the security foundation is solid, I’m excited about the future improvements planned:

Upcoming UX Enhancements

  • GitHub Discussions integration: Moving away from Utterances to GitHub’s native discussion feature for better community engagement and moderation
  • Redesigned reading experience: Complete UX overhaul focusing on readability, accessibility, and modern design principles
  • Enhanced layout architecture:
    • Left sidebar: Table of Contents for easy navigation within posts
    • Right sidebar: Comments section for contextual discussions
    • Responsive design: Seamless experience across desktop, tablet, and mobile
    • Progressive enhancement: Modern features that degrade gracefully

Technical Roadmap

  • Performance optimization: Leveraging Ruby 3.3.x performance gains and modern Jekyll features
  • Enhanced SEO: Building on Jekyll-seo-tag 2.8.0 improvements with structured data
  • Better accessibility: WCAG 2.1 compliance and screen reader optimization
  • Modern web standards: HTTP/3, WebP images, and progressive loading

Content Strategy Evolution

  • More technical deep-dives: Like this security hardening journey
  • Spec-driven development series: Sharing the Kiro methodology and real-world applications
  • Open source contributions: Documenting contributions and community learnings
  • Interactive tutorials: Hands-on guides with embedded examples

Ongoing Maintenance Strategy

The spec process established comprehensive ongoing procedures:

Monthly Security Reviews

  • Monitor GitHub Dependabot alerts
  • Apply critical security patches following established procedures
  • Update security documentation

Quarterly Dependency Updates

  • Update all gems to latest stable versions
  • Execute comprehensive testing protocols
  • Review and optimize version constraints

Annual Major Updates

  • Evaluate Ruby version upgrades
  • Consider Jekyll major version updates
  • Comprehensive security audit and spec review

The Maintenance Procedures

Kiro helped create comprehensive maintenance procedures covering:

  • Emergency Response: Critical vulnerability response within 24 hours
  • Regular Updates: Monthly and quarterly update schedules
  • Testing Protocols: Comprehensive validation checklists
  • Rollback Procedures: Quick recovery from failed updates
  • Documentation Standards: Keeping procedures current

Key Takeaways

For Jekyll Users

  1. Don’t ignore Dependabot alerts: They represent real security risks
  2. Use semantic versioning wisely: ~> x.y allows security patches
  3. Keep Ruby current: EOL versions create cascading problems
  4. Test systematically: Automated + manual validation prevents issues
  5. Document everything: Future you will thank present you

For Developers

  1. Spec-driven development works: Taking time to plan saves time in execution
  2. Requirements matter: Clear acceptance criteria prevent scope creep and missed edge cases
  3. Design upfront: Identifying issues before coding saves debugging time
  4. Maintenance is part of the spec: Don’t just build it, plan to maintain it
  5. Tools like Kiro are game-changers: Structured approaches scale better than ad-hoc methods

The Bottom Line

What started as a comeback blog post became a demonstration of modern development practices. The combination of returning from hiatus with 12 security alerts created the perfect opportunity to try spec-driven development with Kiro.

The systematic approach took more time upfront but resulted in:

  • Zero security vulnerabilities
  • Improved performance and modern dependencies
  • Comprehensive documentation and procedures
  • Clear roadmap for future improvements
  • Confidence in ongoing maintenance

The blog is now running on Ruby 3.3.9 with the latest secure versions of all dependencies. More importantly, I have a proven methodology for future updates—both security and feature enhancements.

This is just the beginning of my return to regular blogging. With a solid, secure foundation and a clear development methodology, I’m excited to continue improving this blog and sharing more technical insights.

The next posts will dive deeper into spec-driven development, the upcoming UX redesign process, and how GitHub Discussions will transform the commenting experience.

I will try to do regular blogging after a multi-year dormancy! 🚀