qastell
v0.9.2
Published
Security audit library for Playwright, Puppeteer, Cypress, and Selenium WebDriver test automation
Maintainers
Readme
QAstell
QA + Castell - Security Auditing for Playwright, Puppeteer, Cypress & Selenium WebDriver
A security audit library that integrates directly into your test automation workflow. Run security checks alongside your functional tests and catch common vulnerabilities early.
The Name: Defense is universal. Castle in English, Kastell in German, Castell in Welsh, Castel in Romanian – all from Latin castellum, the fortress that protects what matters. QAstell brings that same principle to test automation: a fortress built into your CI/CD pipeline.
Why QAstell?
Security Shift-Left
Traditional security testing happens late in the development cycle - often just before release or during dedicated security audits. By this point, vulnerabilities are expensive to fix and may delay releases.
QAstell enables security shift-left by integrating security checks directly into your existing tests:
- SDETs and QA engineers can identify potential security issues during regular test runs
- Developers get immediate feedback when they introduce security regressions
- Security teams can focus on complex, application-specific vulnerabilities instead of chasing common misconfigurations
Complementary, Not Replacement
Important: QAstell is designed to complement, not replace, your existing security tools and practices.
QAstell does not replace SAST tools (SonarQube, Checkmarx), DAST tools (OWASP ZAP, Burp Suite), penetration testing, or security code reviews.
Instead, QAstell fills a gap: continuous, automated detection of common client-side security issues during functional testing. Think of it as an additional safety net that catches low-hanging fruit early, freeing your security specialists to focus on the harder problems.
Who Should Use QAstell?
- SDETs who want to add security value to their test suites
- QA teams looking to catch security regressions before they reach staging
- Development teams practicing DevSecOps
- Small teams without dedicated security resources who want basic coverage
- Anyone who believes security is everyone's responsibility
Features
- Multi-Framework Support - Works with Playwright, Puppeteer, Cypress, and Selenium WebDriver
- 250+ Security Rules - Covers common web security issues across 48 categories
- CVSS Severity Model - Critical/High/Medium/Low/Info with OWASP & CWE references
- Multiple Report Formats - JSON, interactive HTML, and SARIF (GitHub/GitLab compatible)
- Interactive HTML Reports - Filter by severity, category, hide/show rules
- Configurable - Include/exclude rules, set thresholds, skip specific checks
Try It Now - 30 Seconds
No setup needed. Copy, paste, run.
Playwright (one command)
npx -y create-playwright@latest qastell-demo --quiet && cd qastell-demo && npm i qastell && echo 'import{test}from"@playwright/test";import{SecurityAuditor}from"qastell";test("security",async({page})=>{await page.goto("https://example.com");const a=new SecurityAuditor(page);const r=await a.audit();console.log("Issues:",r.summary.total,"| Critical:",r.summary.bySeverity.critical,"| High:",r.summary.bySeverity.high);});' > tests/security.spec.ts && npx playwright test security --reporter=listPuppeteer (one command)
mkdir -p qastell-demo && cd qastell-demo && npm init -y && npm i qastell puppeteer && node -e 'const p=require("puppeteer"),{SecurityAuditor}=require("qastell");(async()=>{const b=await p.launch(),pg=await b.newPage();await pg.goto("https://example.com");const a=new SecurityAuditor(pg),r=await a.audit();console.log("Issues:",r.summary.total,"| Critical:",r.summary.bySeverity.critical,"| High:",r.summary.bySeverity.high);await b.close()})();'Selenium WebDriver (one command)
mkdir -p qastell-demo && cd qastell-demo && npm init -y && npm i qastell selenium-webdriver && node -e 'const{Builder}=require("selenium-webdriver"),chrome=require("selenium-webdriver/chrome"),{SecurityAuditor}=require("qastell");(async()=>{const o=new chrome.Options();o.addArguments("--headless","--no-sandbox");const d=await new Builder().forBrowser("chrome").setChromeOptions(o).build();await d.get("https://example.com");const a=new SecurityAuditor(d),r=await a.audit();console.log("Issues:",r.summary.total,"| Critical:",r.summary.bySeverity.critical,"| High:",r.summary.bySeverity.high);await d.quit()})();'Cypress (one command)
mkdir -p qastell-demo/cypress/e2e && cd qastell-demo && npm init -y && npm i qastell cypress && echo 'const{defineConfig}=require("cypress");module.exports=defineConfig({e2e:{supportFile:false}})' > cypress.config.js && echo 'const{SecurityAuditor}=require("qastell");it("security",()=>{cy.visit("https://example.com");cy.window().then(async(win)=>{const a=new SecurityAuditor(win),r=await a.audit();cy.log("Issues: "+r.summary.total+" | Critical: "+r.summary.bySeverity.critical+" | High: "+r.summary.bySeverity.high)})})' > cypress/e2e/security.cy.js && npx cypress run --spec cypress/e2e/security.cy.jsNote: This one-liner uses JavaScript for simplicity. See the examples for Cypress + TypeScript setups.
Note: First-time Playwright users may need to run
npx playwright installto install browser binaries.
Installation
npm install qastellQuick Start
Playwright
import { test } from '@playwright/test';
import { SecurityAuditor } from 'qastell';
test('security audit', async ({ page }) => {
await page.goto('https://example.com');
const auditor = new SecurityAuditor(page);
await auditor.assertNoViolations();
});Puppeteer
const puppeteer = require('puppeteer');
const { SecurityAuditor } = require('qastell');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
const auditor = new SecurityAuditor(page);
const results = await auditor.audit();
console.log(`Found ${results.summary.total} violations`);
await browser.close();
})();Selenium WebDriver
const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const { SecurityAuditor } = require('qastell');
(async () => {
const options = new chrome.Options();
options.addArguments('--headless', '--no-sandbox');
const driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();
await driver.get('https://example.com');
const auditor = new SecurityAuditor(driver);
const results = await auditor.audit();
console.log(`Found ${results.summary.total} violations`);
await driver.quit();
})();Cypress
import { SecurityAuditor } from 'qastell';
describe('Security', () => {
it('should pass security audit', () => {
cy.visit('https://example.com');
cy.window().then(async (win) => {
const auditor = new SecurityAuditor(win);
const results = await auditor.audit();
cy.log(`Found ${results.summary.total} violations`);
await auditor.assertNoViolations();
});
});
});Framework Compatibility
| Feature | Playwright | Puppeteer | Cypress | WebDriver | |---------|:----------:|:---------:|:-------:|:---------:| | DOM Analysis | ✅ | ✅ | ✅ | ✅ | | Cookie Inspection | ✅ | ✅ | ⚠️* | ✅ | | JavaScript Evaluation | ✅ | ✅ | ✅ | ✅ | | HTTP Response Headers | ✅ | ✅ | ❌ | ❌ | | CSP Meta Tag Detection | ✅ | ✅ | ✅ | ✅ | | Storage Analysis | ✅ | ✅ | ✅ | ✅ | | Shadow DOM Inspection | ✅ | ✅ | ✅ | ✅ | | All 250+ Rules | ✅ | ✅ | ~245** | ~245** |
* Cypress cookie inspection only sees non-httpOnly cookies via the adapter. Use cy.getCookies() for full cookie info.
** Cypress and WebDriver cannot access HTTP response headers, so ~5 header-related rules are skipped automatically.
Usage Examples
Basic Audit
const auditor = new SecurityAuditor(page);
const results = await auditor.audit();
console.log(`Found ${results.summary.total} violations`);
console.log(`By severity:`, results.summary.bySeverity);With Configuration
const results = await auditor.audit({
include: ['links', 'forms'], // Only check these categories
exclude: ['sensitive-autocomplete'], // Skip this category
skipRules: ['missing-noopener'], // Skip specific rules
thresholds: {
critical: 0, // Fail if any critical issues
high: 2, // Allow up to 2 high severity issues
}
});Assertion with Allowed Violations
await auditor.assertNoViolations({
include: ['links', 'forms'],
allowedViolations: ['missing-noopener'], // Ignore this specific rule
});Generate Reports
const results = await auditor.audit();
// Full interactive HTML report (all tiers)
const html = results.toHTML();
fs.writeFileSync('report.html', html);
// Compact summary HTML (smaller, faster - great for attachments)
const summary = results.toSummaryHTML();
fs.writeFileSync('summary.html', summary);
// JSON report (Enterprise+)
const json = results.toJSON();
fs.writeFileSync('report.json', json);
// SARIF report (Corporate only - GitHub/GitLab compatible)
const sarif = results.toSARIF();
fs.writeFileSync('report.sarif', sarif);Playwright Test Attachments
Attach the HTML report directly to Playwright's test reporter - the report appears as a clickable link in Playwright's HTML report:
import { test } from '@playwright/test';
import { SecurityAuditor } from 'qastell';
test('security audit', async ({ page }, testInfo) => {
await page.goto('https://example.com');
const auditor = new SecurityAuditor(page);
const results = await auditor.audit();
// Attach report to Playwright's HTML reporter
await results.attachReport(testInfo);
// Or with a custom name
await results.attachReport(testInfo, 'homepage-security');
});Run with npx playwright test --reporter=html and click "security-report" in the test details to view the full QAstell report.
Playwright Fixture (Auto-Attach)
For automatic report attachment, use the QAstell Playwright fixture. Reports are automatically attached to every test that runs a security audit:
// Use QAstell's test fixture instead of Playwright's
import { test, expect } from 'qastell/fixtures/playwright';
test('security audit', async ({ page, securityAuditor }) => {
await page.goto('https://example.com');
const results = await securityAuditor.audit();
expect(results.passed()).toBe(true);
// Report is automatically attached - no need to call attachReport()!
});The fixture:
- Provides a pre-configured
securityAuditorinstance tied to yourpage - Automatically attaches the HTML report after each test that calls
audit() - Only attaches when an audit was run (tests without audits won't have attachments)
Security Rules (250+ rules across 48 categories)
QAstell checks for common security issues including:
| Category | Example Rules |
|----------|--------------|
| Links | Missing rel="noopener", JavaScript URLs |
| Forms | Missing CSRF tokens, insecure form actions |
| Headers | Missing CSP, X-Frame-Options, HSTS |
| Cookies | Missing Secure/HttpOnly/SameSite flags |
| XSS Vectors | Inline handlers, innerHTML sinks, eval() |
| DOM Security | DOM clobbering, prototype pollution |
| Third-Party | Ad network scripts without SRI |
| Storage | Sensitive data in localStorage |
| CORS | Wildcard origins, credential leaks |
| And more... | Shadow DOM, WebSockets, Service Workers, etc. |
Each rule includes OWASP Top 10 and CWE references, plus remediation guidance.
| Rule ID | Category | Severity | Description |
|---------|----------|----------|-------------|
| missing-noopener | links | medium | Links with target="_blank" missing rel="noopener" |
| javascript-url | links | high | Links using javascript: URLs (XSS vector) |
| missing-csrf-token | forms | high | POST forms without CSRF token protection |
| sensitive-autocomplete | forms | low | Password/credit card fields allowing autocomplete |
| insecure-form-action | forms | high | HTTPS page with HTTP form action |
| inline-event-handlers | inline-handlers | medium | Inline event handlers (onclick, onload, etc.) |
| missing-iframe-sandbox | iframes | medium | Cross-origin iframes missing sandbox attribute |
| dangerous-iframe-sandbox | iframes | high | Iframes with allow-scripts + allow-same-origin |
| mixed-content | mixed-content | high | HTTP resources loaded on HTTPS pages |
| missing-csp-meta | headers | info | No Content Security Policy meta tag |
| base-tag-detected | headers | info | Base tag present (potential hijacking vector) |
| no-frame-protection-meta | headers | info | No clickjacking protection in meta tags |
| missing-sri | sri | medium | External scripts/stylesheets without integrity attribute |
| weak-sri-hash | sri | low | SRI using only SHA-256 instead of SHA-384/512 |
| exposed-email | sensitive-data | low | Email addresses exposed in page source |
| exposed-api-key | sensitive-data | high | Potential API keys/tokens in page source |
| comments-with-secrets | sensitive-data | low | HTML comments with sensitive content |
| shadow-dom-inline-handlers | shadow-dom | high | Inline handlers in Shadow DOM (CSP bypass) |
| open-shadow-dom | shadow-dom | info | Open Shadow DOM with forms/sensitive inputs |
| shadow-dom-form-no-csrf | shadow-dom | high | Shadow DOM forms without CSRF protection |
| dom-clobbering-globals | dom-clobbering | high | Element IDs shadowing global properties |
| dom-clobbering-collections | dom-clobbering | medium | Multiple elements creating exploitable collections |
| form-property-clobbering | dom-clobbering | high | Form inputs clobbering form properties |
| ad-network-no-sri | third-party | high | Ad network scripts without SRI |
| ad-iframe-no-sandbox | third-party | medium | Ad iframes without sandbox |
| third-party-document-write | third-party | medium | Third-party scripts using document.write |
| excessive-third-party | third-party | info | Too many third-party script domains |
| tracking-on-sensitive-page | third-party | medium | Tracking scripts on login/payment pages |
| script-wrong-extension | mime-type | medium | Scripts with non-JS extensions |
| stylesheet-wrong-extension | mime-type | low | Stylesheets with non-CSS extensions |
| script-type-misuse | mime-type | low | Non-executing script types with code |
| data-uri-script | mime-type | high | Scripts loaded from data: URIs |
| url-prototype-pollution | prototype-pollution | high | URL contains proto/constructor patterns |
| unsafe-object-merge | prototype-pollution | medium | Scripts merging external data unsafely |
| polluted-prototype | prototype-pollution | critical | Object.prototype has been polluted |
| modifies-prototype | prototype-pollution | medium | Script modifies built-in prototypes |
| link-redirect-parameter | open-redirect | medium | Links with redirect URL parameters |
| form-redirect-parameter | open-redirect | medium | Forms with redirect parameters |
| meta-refresh-redirect | open-redirect | medium | Meta refresh to external URL |
| js-location-redirect | open-redirect | high | JavaScript redirect using URL input |
| opener-accessible | open-redirect | low | window.opener accessible (tabnabbing) |
| postmessage-no-origin-check | postmessage | high | postMessage listener without origin validation |
| postmessage-wildcard-origin | postmessage | medium | postMessage uses wildcard (*) target origin |
| postmessage-dangerous-sink | postmessage | high | postMessage handler uses dangerous sinks |
| localstorage-sensitive-data | storage | high | Sensitive data detected in localStorage |
| sessionstorage-sensitive-data | storage | medium | Sensitive data detected in sessionStorage |
| storage-unencrypted-data | storage | info | Large unencrypted data in web storage |
| storage-no-error-handling | storage | low | Storage access without error handling |
| dangling-markup-attribute | dangling-markup | high | Potentially dangling markup detected |
| base-tag-hijacking | dangling-markup | critical | Base tag points to external origin |
| form-external-action | dangling-markup | medium | Form submits to external domain |
| meta-refresh-exfiltration | dangling-markup | high | Meta refresh with potential data exfiltration |
| noscript-dangling-markup | dangling-markup | low | Noscript tag with suspicious content |
| css-external-import | css-injection | medium | CSS @import from external source |
| css-external-url | css-injection | low | CSS url() references external domain |
| css-attribute-selector | css-injection | medium | CSS attribute selector could leak data |
| css-expression | css-injection | high | CSS expression or behavior detected |
| idn-homograph | unicode | high | IDN homograph attack in link |
| rtl-override | unicode | high | Right-to-left override character detected |
| null-byte-url | unicode | high | Null byte or dangerous character in URL |
| invisible-characters | unicode | low | Invisible/zero-width characters detected |
| innerhtml-sink | html-injection | high | innerHTML used with external data |
| document-write-sink | html-injection | medium | document.write usage detected |
| eval-usage | html-injection | high | eval() or similar function detected |
| user-controlled-selector | html-injection | medium | querySelector with external input |
| template-literal-injection | html-injection | medium | Template literal with external data |
| service-worker-cross-origin | service-worker | critical | Service worker from different origin |
| service-worker-broad-scope | service-worker | info | Service worker with root scope |
| service-worker-no-https | service-worker | medium | Service worker on non-HTTPS page |
| service-worker-suspicious-patterns | service-worker | medium | Suspicious service worker patterns |
| fullscreen-abuse | clickjacking | medium | Fullscreen API usage detected |
| pointer-events-manipulation | clickjacking | low | Pointer-events manipulation detected |
| opacity-hiding | clickjacking | medium | Low opacity element covering content |
| user-select-manipulation | clickjacking | low | Text selection disabled |
| cookie-no-httponly | cookies | high | Sensitive cookie accessible to JS |
| cookie-no-secure | cookies | medium | Cookie without Secure flag on HTTPS |
| cookie-no-samesite | cookies | medium | Cookie without SameSite attribute |
| cookie-broad-scope | cookies | low | Cookie with overly broad scope |
| cookie-sensitive-value | cookies | medium | Cookie may contain sensitive data |
| websocket-insecure | websocket | high | Unencrypted WebSocket on HTTPS |
| websocket-cross-origin | websocket | medium | WebSocket to different origin |
| websocket-no-auth | websocket | medium | WebSocket without authentication |
| websocket-message-sink | websocket | high | WebSocket message in dangerous sink |
| svg-script-content | mutation-xss | high | SVG contains script or handlers |
| mathml-namespace-abuse | mutation-xss | medium | MathML namespace confusion |
| nested-parsing-abuse | mutation-xss | medium | Dangerous element nesting pattern |
| style-tag-abuse | mutation-xss | low | Style tag in unusual context |
| unsafe-dom-parsing | mutation-xss | high | DOMParser without sanitization |
| private-ip-access | dns-rebinding | high | Request to private IP range detected |
| host-header-unvalidated | dns-rebinding | medium | Host header validation missing |
| websocket-rebinding-risk | dns-rebinding | medium | WebSocket rebinding vulnerability |
| cors-rebinding-risk | dns-rebinding | high | CORS config enables rebinding |
| unsafe-string-comparison | timing-attacks | medium | Timing-vulnerable comparison |
| early-return-auth | timing-attacks | low | Early return in auth function |
| high-resolution-timing | timing-attacks | info | High-resolution timing API usage |
| array-length-timing | timing-attacks | low | Array iteration timing leak |
| missing-vary-header | cache-poisoning | medium | Missing Vary header on dynamic content |
| unkeyed-header-reflection | cache-poisoning | high | Unkeyed header reflected in response |
| cache-control-misconfigured | cache-poisoning | medium | Cache-Control misconfiguration |
| parameter-cache-reflection | cache-poisoning | high | URL param reflected in cached response |
| fat-get-request | cache-poisoning | low | GET request with body (fat GET) |
| cloud-resource-unclaimed | subdomain-takeover | high | Reference to claimable cloud resource |
| dangling-cname-js | subdomain-takeover | medium | Dangling CNAME in JavaScript |
| broken-external-resource | subdomain-takeover | high | Broken external resource reference |
| mail-subdomain-reference | subdomain-takeover | medium | Mail service subdomain reference |
| cors-wildcard-credentials | cors | critical | CORS wildcard with credentials |
| cors-null-origin | cors | high | CORS accepts null origin |
| cors-origin-reflection | cors | high | CORS reflects origin header |
| cors-preflight-permissive | cors | medium | Overly permissive CORS preflight |
| cors-internal-access | cors | high | CORS request to internal network |
| content-length-manipulation | request-smuggling | high | Content-Length header manipulation |
| transfer-encoding-usage | request-smuggling | medium | Transfer-Encoding header usage |
| ambiguous-request | request-smuggling | medium | Ambiguous HTTP request pattern |
| http2-downgrade | request-smuggling | low | HTTP/2 downgrade pattern |
| json-parse-reviver | deserialization | medium | JSON.parse with reviver function |
| function-constructor-deser | deserialization | high | Function constructor deserialization |
| eval-deserialization | deserialization | critical | Eval-based deserialization |
| jquery-deser-methods | deserialization | medium | jQuery unsafe deserialization |
| postmessage-deser | deserialization | high | PostMessage data deserialization |
| toctou-pattern | race-condition | medium | Time-of-check-time-of-use pattern |
| unsync-shared-state | race-condition | medium | Unsynchronized shared state |
| double-submit-risk | race-condition | low | Double submit vulnerability |
| parallel-fetch-race | race-condition | low | Parallel fetch race condition |
| localstorage-race | race-condition | low | LocalStorage race condition |
| missing-permissions-policy | permissions-policy | medium | Missing Permissions Policy |
| iframe-allow-permissive | permissions-policy | medium | Overly permissive iframe allow |
| autoplay-without-policy | permissions-policy | low | Autoplay without policy |
| sensor-access | permissions-policy | low | Motion sensor access |
| document-domain-usage | permissions-policy | high | document.domain usage |
| math-random-security | crypto-weaknesses | high | Math.random for security |
| hardcoded-crypto-secrets | crypto-weaknesses | critical | Hardcoded cryptographic secrets |
| deprecated-crypto-api | crypto-weaknesses | high | Deprecated crypto API usage |
| weak-password-hashing | crypto-weaknesses | high | Weak password hashing |
| crypto-subtle-validation | crypto-weaknesses | medium | Web Crypto API issues |
| catastrophic-backtracking | redos | high | Catastrophic backtracking regex |
| unbounded-regex-input | redos | high | Unbounded regex on user input |
| email-regex-redos | redos | medium | Vulnerable email regex |
| url-regex-redos | redos | medium | Vulnerable URL regex |
| regex-from-input | redos | critical | Regex created from user input |
| internal-package-reference | dependency-confusion | medium | Internal package name reference |
| uncommon-cdn-package | dependency-confusion | low | Uncommon CDN package |
| dynamic-import-risk | dependency-confusion | medium | Dynamic import risk |
| scoped-package-risk | dependency-confusion | high | Scoped package without registry lock |
| url-param-to-fetch | ssrf | high | URL parameter passed to fetch |
| user-input-url | ssrf | high | User input in URL construction |
| proxy-endpoint-usage | ssrf | medium | Proxy endpoint usage |
| webhook-config | ssrf | medium | Webhook configuration interface |
| media-url-from-input | ssrf | medium | Media URL from user input |
| window-opener-access | tabnabbing | high | window.opener access |
| window-open-no-noopener | tabnabbing | medium | window.open without noopener |
| cross-origin-frame-access | tabnabbing | medium | Cross-origin frame access |
| postmessage-to-opener | tabnabbing | medium | postMessage to opener |
| sensitive-data-new-tab | tabnabbing | low | Sensitive data in new tab link |
| polyglot-file-pattern | content-type | medium | Polyglot file pattern |
| jsonp-endpoint | content-type | medium | JSONP endpoint |
| data-url-executable | content-type | high | Executable data URL |
| blob-url-xss | content-type | medium | Blob URL XSS risk |
| mime-sniffing-risk | content-type | low | MIME sniffing risk |
HTML Report Features
The interactive HTML report includes:
- Summary Cards - Click to filter by All/Passed/Failed/Critical
- Category Filters - Toggle visibility by rule category
- Hide/Show Rules - Hide individual rules, restore from dropdown
- Remediation - How-to-fix guidance for each violation
- View Source - See full element HTML with DevTools tips
Reporter Integration
QAstell provides a flexible architecture for integrating security results into various test reporters.
Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ AuditResults │
│ (from SecurityAuditor.audit()) │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ ReportConnector │
│ (unified attachment API) │
└──────────┬──────────────────┴───────────────────┬───────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────────┐
│ Formatters │ │ Adapters │
│ (output generation) │ │ (reporter integration) │
├──────────────────────┤ ├──────────────────────────┤
│ • html │ │ • allure (Allure 2/3) │
│ • htmlSummary │ │ • playwright (testInfo) │
│ • markdown │ │ • cucumber (World) │
│ • json (Enterprise+) │ │ • file (save to disk) │
│ • sarif (Corporate) │ └──────────────────────────┘
│ • junit │
│ • cucumber │
└──────────────────────┘Quick Start with ReportConnector
import { SecurityAuditor } from 'qastell';
import { ReportConnector, adapters } from 'qastell/reporters';
// 1. Create adapter for your reporter
const adapter = adapters.allure(allure);
// 2. Create connector
const connector = new ReportConnector(adapter);
// 3. Run audit and attach results
const results = await auditor.audit();
await connector.attach(results, {
inline: 'markdown', // Show markdown summary inline
attachments: ['html'], // Attach full HTML report
});Available Adapters
| Adapter | Usage | Description |
|---------|-------|-------------|
| adapters.allure(allure) | Allure 2/3 | Labels, descriptions, attachments |
| adapters.playwright(testInfo) | Playwright | Test attachments |
| adapters.cucumber(world) | Cucumber | Scenario embeddings |
| adapters.file(outputDir) | Any | Save reports to disk |
Legacy Connectors
The legacy connector classes (PlaywrightConnector, AllureConnector, CucumberConnector) are still available for backward compatibility:
import { CucumberConnector } from 'qastell/connectors';
const connector = new CucumberConnector();
await connector.attachSummary(results, this); // 'this' is Cucumber WorldSee the examples for complete integration patterns.
CI/CD Integration
GitHub Actions with SARIF
- name: Run security audit
run: npx playwright test security-audit
- name: Upload SARIF report
if: always()
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: reports/security.sarif
category: qastell
continue-on-error: trueSee it in action — The qastell-community repo runs QAstell in CI against a real demo site with Playwright and Puppeteer. Browse the latest reports on GitHub Pages, or view the workflow source.
GitLab CI
security_audit:
script:
- npm run test:security
artifacts:
reports:
sast: reports/security.sarifLicensing
QAstell offers three tiers:
| Tier | Scans/Day | Report Formats | Price | |------|-----------|----------------|-------| | Free (Non-Commercial) | 10 | HTML | €0 | | Enterprise | 100 | HTML, JSON | €99/month | | Corporate | Unlimited | HTML, JSON, SARIF | €499/month |
Note: The free tier is for non-commercial use only (personal projects, open source, learning). For commercial use, please purchase an Enterprise or Corporate license.
Using a License Key
import { initLicense, SecurityAuditor } from 'qastell';
// Initialize once at startup
initLicense(process.env.QASTELL_LICENSE);
// Then use normally - quota is tracked automatically
const auditor = new SecurityAuditor(page);
await auditor.audit();Check License Status
# Check current license status
npx qastell license-info
# Check a specific license key
QASTELL_LICENSE="..." npx qastell license-infoAPI Reference
SecurityAuditor
constructor(page: Page, options?: SecurityAuditorOptions)Methods
audit(config?: AuditConfig): Promise<AuditResults>- Run security auditassertNoViolations(options?: AssertionOptions): Promise<void>- Assert no violations (throws on failure)getFramework(): string- Get detected framework name
AuditResults
raw: AuditResult- Raw audit dataviolations- All violations with rule metadatasummary- Counts by severity and categorypassed(): boolean- Check if audit passed thresholdstoJSON(): string- Generate JSON report (Enterprise+)toHTML(): string- Generate full interactive HTML reporttoSummaryHTML(): string- Generate compact summary HTML (smaller, faster)toSARIF(): string- Generate SARIF report (Corporate only)attachReport(testInfo, name?)- Attach HTML report to Playwright test (appears in HTML reporter)
Types
interface AuditConfig {
include?: RuleCategory[];
exclude?: RuleCategory[];
skipRules?: string[];
thresholds?: Partial<Record<CVSSSeverity, number>>;
}
type CVSSSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';Learn More
- Website: qastell.eu
- Documentation: qastell.eu/docs
- Playwright Guide: qastell.eu/docs-playwright
- Puppeteer Guide: qastell.eu/docs-puppeteer
- Cypress Guide: qastell.eu/docs-cypress
- WebDriver Guide: qastell.eu/docs-webdriver
- Examples: GitHub
- Live Reports: GitHub Pages
- Report Issues: GitHub Issues
License
See qastell.eu/terms for details. QAstell is free for non-commercial use. Commercial use requires a paid license.
Made in the 🇪🇺 with ❤️ for people, environment, and diversity.
