solidstats 0.0.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +61 -5
- data/README.md +52 -5
- data/app/controllers/solidstats/dashboard_controller.rb +43 -123
- data/app/services/solidstats/audit_service.rb +56 -0
- data/app/services/solidstats/data_collector_service.rb +83 -0
- data/app/services/solidstats/todo_service.rb +114 -0
- data/app/views/solidstats/dashboard/_todos.html.erb +6 -27
- data/app/views/solidstats/dashboard/audit/_additional_styles.css +22 -0
- data/app/views/solidstats/dashboard/audit/_audit_details.html.erb +47 -58
- data/app/views/solidstats/dashboard/audit/_audit_summary.html.erb +1 -1
- data/app/views/solidstats/dashboard/audit/_security_audit.html.erb +0 -23
- data/app/views/solidstats/dashboard/audit/_vulnerabilities_table.html.erb +1092 -38
- data/app/views/solidstats/dashboard/audit/_vulnerability_details.html.erb +4 -3
- data/app/views/solidstats/dashboard/index.html.erb +1152 -162
- data/config/routes.rb +1 -0
- data/lib/solidstats/version.rb +1 -1
- data/lib/tasks/solidstats_release.rake +69 -0
- metadata +10 -5
- data/app/views/solidstats/dashboard/audit/_audit_filters.html.erb +0 -5
| @@ -1,11 +1,10 @@ | |
| 1 | 
            -
            <div id="audit-details" | 
| 1 | 
            +
            <div id="audit-details">
         | 
| 2 2 | 
             
              <%= render partial: 'solidstats/dashboard/audit/audit_summary' %>
         | 
| 3 3 |  | 
| 4 4 | 
             
              <% results = @audit_output.dig("results") || [] %>
         | 
| 5 5 | 
             
              <% if results.empty? %>
         | 
| 6 6 | 
             
                <%= render partial: 'solidstats/dashboard/audit/no_vulnerabilities' %>
         | 
| 7 7 | 
             
              <% else %>
         | 
| 8 | 
            -
                <%= render partial: 'solidstats/dashboard/audit/audit_filters' %>
         | 
| 9 8 | 
             
                <%= render partial: 'solidstats/dashboard/audit/vulnerabilities_table', locals: { results: results } %>
         | 
| 10 9 | 
             
                <%= render partial: 'solidstats/dashboard/audit/vulnerability_details', locals: { results: results } %>
         | 
| 11 10 | 
             
              <% end %>
         | 
| @@ -159,12 +158,6 @@ | |
| 159 158 | 
             
                border-left: 4px solid #dc3545;
         | 
| 160 159 | 
             
              }
         | 
| 161 160 |  | 
| 162 | 
            -
              .vulnerability-title {
         | 
| 163 | 
            -
                margin-top: 0;
         | 
| 164 | 
            -
                margin-bottom: 0.75rem;
         | 
| 165 | 
            -
                font-size: 1.1rem;
         | 
| 166 | 
            -
                font-weight: 600;
         | 
| 167 | 
            -
              }
         | 
| 168 161 |  | 
| 169 162 | 
             
              .vulnerability-meta {
         | 
| 170 163 | 
             
                display: flex;
         | 
| @@ -224,6 +217,7 @@ | |
| 224 217 | 
             
                border-bottom: 1px solid #e1e4e8;
         | 
| 225 218 | 
             
                padding: 24px;
         | 
| 226 219 | 
             
                background-color: #ffffff;
         | 
| 220 | 
            +
                transition: background-color 0.3s ease;
         | 
| 227 221 | 
             
              }
         | 
| 228 222 |  | 
| 229 223 | 
             
              .md-entry:last-child {
         | 
| @@ -234,6 +228,10 @@ | |
| 234 228 | 
             
                border-left: 4px solid #d73a49;
         | 
| 235 229 | 
             
              }
         | 
| 236 230 |  | 
| 231 | 
            +
              .md-entry.highlight-detail {
         | 
| 232 | 
            +
                background-color: #fffbe6;
         | 
| 233 | 
            +
              }
         | 
| 234 | 
            +
              
         | 
| 237 235 | 
             
              /* Markdown heading style */
         | 
| 238 236 | 
             
              .md-heading {
         | 
| 239 237 | 
             
                font-size: 1.5rem;
         | 
| @@ -416,12 +414,12 @@ | |
| 416 414 | 
             
                const toggleBtn = document.querySelector('.toggle-details-btn');
         | 
| 417 415 | 
             
                if (toggleBtn) {
         | 
| 418 416 | 
             
                  toggleBtn.addEventListener('click', function() {
         | 
| 419 | 
            -
                    const detailsSection = document.querySelector('. | 
| 420 | 
            -
                    const isHidden = detailsSection. | 
| 417 | 
            +
                    const detailsSection = document.querySelector('.md-container');
         | 
| 418 | 
            +
                    const isHidden = detailsSection.style.display === 'none';
         | 
| 421 419 |  | 
| 422 420 | 
             
                    if (isHidden) {
         | 
| 423 421 | 
             
                      // Show the details
         | 
| 424 | 
            -
                      detailsSection. | 
| 422 | 
            +
                      detailsSection.style.display = '';
         | 
| 425 423 | 
             
                      this.textContent = 'Hide Details';
         | 
| 426 424 | 
             
                      this.setAttribute('aria-expanded', 'true');
         | 
| 427 425 |  | 
| @@ -432,7 +430,7 @@ | |
| 432 430 | 
             
                      });
         | 
| 433 431 | 
             
                    } else {
         | 
| 434 432 | 
             
                      // Hide the details
         | 
| 435 | 
            -
                      detailsSection. | 
| 433 | 
            +
                      detailsSection.style.display = 'none';
         | 
| 436 434 | 
             
                      this.textContent = 'Show Details';
         | 
| 437 435 | 
             
                      this.setAttribute('aria-expanded', 'false');
         | 
| 438 436 | 
             
                    }
         | 
| @@ -440,66 +438,57 @@ | |
| 440 438 | 
             
                }
         | 
| 441 439 |  | 
| 442 440 | 
             
                // View details link functionality - simplified to avoid duplication
         | 
| 443 | 
            -
                document.querySelectorAll('.view-details-link').forEach(function(link) {
         | 
| 441 | 
            +
                document.querySelectorAll('.view-details-link, .scroll-to-details').forEach(function(link) {
         | 
| 444 442 | 
             
                  link.addEventListener('click', function(e) {
         | 
| 445 443 | 
             
                    e.preventDefault();
         | 
| 446 444 | 
             
                    const target = this.getAttribute('href');
         | 
| 447 | 
            -
                    const  | 
| 445 | 
            +
                    const mdContainer = document.querySelector('.md-container');
         | 
| 448 446 |  | 
| 449 447 | 
             
                    // Show the details section if it's hidden
         | 
| 450 | 
            -
                    if ( | 
| 451 | 
            -
                       | 
| 448 | 
            +
                    if (mdContainer.style.display === 'none') {
         | 
| 449 | 
            +
                      mdContainer.style.display = '';
         | 
| 452 450 | 
             
                      const toggleBtn = document.querySelector('.toggle-details-btn');
         | 
| 453 | 
            -
                      toggleBtn | 
| 454 | 
            -
             | 
| 451 | 
            +
                      if (toggleBtn) {
         | 
| 452 | 
            +
                        toggleBtn.textContent = 'Hide Details';
         | 
| 453 | 
            +
                        toggleBtn.setAttribute('aria-expanded', 'true');
         | 
| 454 | 
            +
                      }
         | 
| 455 455 | 
             
                    }
         | 
| 456 456 |  | 
| 457 457 | 
             
                    // Scroll to the specific vulnerability
         | 
| 458 458 | 
             
                    setTimeout(() => {
         | 
| 459 | 
            -
                      document.querySelector(target) | 
| 460 | 
            -
             | 
| 461 | 
            -
                         | 
| 462 | 
            -
             | 
| 459 | 
            +
                      const targetElement = document.querySelector(target);
         | 
| 460 | 
            +
                      if (targetElement) {
         | 
| 461 | 
            +
                        const headerOffset = 70;
         | 
| 462 | 
            +
                        const elementPosition = targetElement.getBoundingClientRect().top;
         | 
| 463 | 
            +
                        const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
         | 
| 464 | 
            +
                        
         | 
| 465 | 
            +
                        window.scrollTo({
         | 
| 466 | 
            +
                          top: offsetPosition,
         | 
| 467 | 
            +
                          behavior: "smooth"
         | 
| 468 | 
            +
                        });
         | 
| 469 | 
            +
                        
         | 
| 470 | 
            +
                        // Highlight the target element briefly
         | 
| 471 | 
            +
                        targetElement.classList.add('highlight-detail');
         | 
| 472 | 
            +
                        setTimeout(() => {
         | 
| 473 | 
            +
                          targetElement.classList.remove('highlight-detail');
         | 
| 474 | 
            +
                        }, 1500);
         | 
| 475 | 
            +
                      }
         | 
| 463 476 | 
             
                    }, 100);
         | 
| 464 477 | 
             
                  });
         | 
| 465 478 | 
             
                });
         | 
| 466 479 |  | 
| 467 | 
            -
                //  | 
| 468 | 
            -
                document. | 
| 469 | 
            -
                   | 
| 470 | 
            -
             | 
| 471 | 
            -
                    
         | 
| 472 | 
            -
                     | 
| 473 | 
            -
                     | 
| 474 | 
            -
             | 
| 475 | 
            -
             | 
| 476 | 
            -
             | 
| 477 | 
            -
             | 
| 478 | 
            -
             | 
| 479 | 
            -
                      document.querySelectorAll('.vulnerability-row').forEach(function(row) {
         | 
| 480 | 
            -
                        const gem = row.cells[0].textContent.trim();
         | 
| 481 | 
            -
                        const solution = row.querySelector('.solution code')?.textContent.trim() || "No patch available";
         | 
| 482 | 
            -
                        solutions.push(`${gem}: ${solution}`);
         | 
| 483 | 
            -
                      });
         | 
| 484 | 
            -
                      
         | 
| 485 | 
            -
                      // Copy all solutions to clipboard
         | 
| 486 | 
            -
                      navigator.clipboard.writeText(solutions.join('\n')).then(() => {
         | 
| 487 | 
            -
                        alert('All solutions copied to clipboard!');
         | 
| 488 | 
            -
                      });
         | 
| 489 | 
            -
                      
         | 
| 490 | 
            -
                      return;
         | 
| 491 | 
            -
                    }
         | 
| 492 | 
            -
                    
         | 
| 493 | 
            -
                    // Filter the vulnerability rows
         | 
| 494 | 
            -
                    document.querySelectorAll('.vulnerability-row').forEach(function(row) {
         | 
| 495 | 
            -
                      const isHigh = row.classList.contains('high-severity');
         | 
| 496 | 
            -
                      
         | 
| 497 | 
            -
                      if (filter === 'all') {
         | 
| 498 | 
            -
                        row.style.display = '';
         | 
| 499 | 
            -
                      } else if (filter === 'high') {
         | 
| 500 | 
            -
                        row.style.display = isHigh ? '' : 'none';
         | 
| 501 | 
            -
                      }
         | 
| 502 | 
            -
                    });
         | 
| 480 | 
            +
                // Special case handler for copy-solutions button (if it exists)
         | 
| 481 | 
            +
                document.querySelector('.filter-btn[data-filter="copy-solutions"]')?.addEventListener('click', function() {
         | 
| 482 | 
            +
                  const solutions = [];
         | 
| 483 | 
            +
                  document.querySelectorAll('.vulnerability-row').forEach(function(row) {
         | 
| 484 | 
            +
                    const gem = row.cells[0].textContent.trim();
         | 
| 485 | 
            +
                    const solution = row.querySelector('.solution code')?.textContent.trim() || "No patch available";
         | 
| 486 | 
            +
                    solutions.push(`${gem}: ${solution}`);
         | 
| 487 | 
            +
                  });
         | 
| 488 | 
            +
                  
         | 
| 489 | 
            +
                  // Copy all solutions to clipboard
         | 
| 490 | 
            +
                  navigator.clipboard.writeText(solutions.join('\n')).then(() => {
         | 
| 491 | 
            +
                    alert('All solutions copied to clipboard!');
         | 
| 503 492 | 
             
                  });
         | 
| 504 493 | 
             
                });
         | 
| 505 494 | 
             
              });
         | 
| @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            <%# filepath: /Users/mezbah/microstartup/infolily_organizer/gems/solidstats/app/views/solidstats/dashboard/_audit_summary.html.erb %>
         | 
| 2 2 | 
             
            <div class="audit-summary">
         | 
| 3 3 | 
             
              <div class="audit-summary-header">
         | 
| 4 | 
            -
                < | 
| 4 | 
            +
                <h2><span class="icon">🔒</span>Security Audit Summary</h2>
         | 
| 5 5 | 
             
                <% created_at = @audit_output.dig("created_at") %>
         | 
| 6 6 | 
             
                <span class="audit-date">Last updated: <%= created_at ? DateTime.parse(created_at).strftime("%B %d, %Y at %H:%M") : Time.now.strftime("%B %d, %Y at %H:%M") %></span>
         | 
| 7 7 | 
             
              </div>
         | 
| @@ -1,27 +1,4 @@ | |
| 1 1 | 
             
            <div class="stat-card audit-card">
         | 
| 2 | 
            -
              <h2><span class="icon">🔒</span> Security Audit</h2>
         | 
| 3 | 
            -
              <div class="card-content">
         | 
| 4 | 
            -
                <% results = @audit_output.dig("results") || [] %>
         | 
| 5 | 
            -
                <% vulnerabilities_count = results.size %>
         | 
| 6 | 
            -
                <% if vulnerabilities_count == 0 %>
         | 
| 7 | 
            -
                  <div class="status-badge success">All Clear</div>
         | 
| 8 | 
            -
                <% else %>
         | 
| 9 | 
            -
                  <div class="status-badge warning"><%= vulnerabilities_count %> <%= "Vulnerability".pluralize(vulnerabilities_count) %> Found</div>
         | 
| 10 | 
            -
                <% end %>
         | 
| 11 | 
            -
                
         | 
| 12 | 
            -
                <div class="metrics-group">
         | 
| 13 | 
            -
                  <div class="metric">
         | 
| 14 | 
            -
                    <span class="metric-label">Affected Gems:</span>
         | 
| 15 | 
            -
                    <span class="metric-value"><%= results.map { |r| r.dig("gem", "name") }.uniq.size %></span>
         | 
| 16 | 
            -
                  </div>
         | 
| 17 | 
            -
                  <div class="metric">
         | 
| 18 | 
            -
                    <span class="metric-label">High Severity:</span>
         | 
| 19 | 
            -
                    <span class="metric-value"><%= results.count { |r| %w[high critical].include?(r.dig("advisory", "criticality").to_s.downcase) } %></span>
         | 
| 20 | 
            -
                  </div>
         | 
| 21 | 
            -
                </div>
         | 
| 22 | 
            -
                
         | 
| 23 | 
            -
                <a href="#" class="toggle-details" data-target="audit-details">Show Details</a>
         | 
| 24 | 
            -
              </div>
         | 
| 25 2 | 
             
              <%= render 'solidstats/dashboard/audit/audit_details' %>
         | 
| 26 3 | 
             
            </div>
         | 
| 27 4 |  |