QWED
v7.2.2 GitHub 60

Type to search pages, engines and advisories.

    Looking for API details?Search the docs

    GHSA-pr29-r6fv-grmq

    ClassificationGuard certifies a contractor claim against employee facts (false VERIFIED via claim normalisation and unchecked facts)

    Summary

    ClassificationGuard.verify_classification_claim (worker classification under IRS common-law indicators) can certify the wrong worker status as VERIFIED, with a proof_ref. It is also reachable through TaxPreFlight.audit_transaction with action: "hire", which then returns allowed: true.

    There are two independent causes.

    1. Claim normalisation by substring, with the employee check first. The free-text claim is upper-cased and then tested with "W-2" in claim or "EMPLOYEE" in claim before "1099" in claim or "CONTRACTOR" in claim. A contractor claim that contains the word "employee" normalises to W2. That covers common forms such as "non-employee", "Non-Employee Compensation (1099-NEC)" and "not an employee". Against facts that the guard itself classifies as an employee (W2), such a claim matches and is certified.
    2. Facts are read with facts.get(key, False) and evaluated by truthiness. A missing fact counts as "no employee indicator", so verify_classification_claim("1099", {}) derives 1099 and is certified. Non-boolean values are accepted, so the strings "no" / "false" count as True.

    Impact

    The guard exists to stop a worker who is an employee on the facts from being paid as a 1099 contractor, which means no withholding. An AI agent, or anyone who can influence the agent's output, can get a contractor classification certified for a worker the guard derives as an employee, just by phrasing the claim as "1099 non-employee contractor". The result carries status: VERIFIED and an authoritative proof_ref, so a downstream gate that follows the documented contract admits it.

    Missing or wrongly typed facts are certified the same way when the guard is called directly. Through TaxPreFlight, missing facts are blocked by the required-field check, but string facts are not.

    No code execution or data disclosure. The impact is on the integrity of the verification verdict.

    Proof of concept

    qwed-tax==0.2.0 from PyPI, Python 3.11:

    from qwed_tax.guards.classification_guard import ClassificationGuard
    from qwed_tax.verifier import TaxPreFlight
    
    g = ClassificationGuard()
    employee_facts = {"provides_tools": True, "reimburses_expenses": True, "indefinite_relationship": True}
    
    # Control: plain "1099" against employee facts is correctly rejected
    assert not g.verify_classification_claim("1099", employee_facts)["verified"]
    
    # Contractor claims containing "employee" are certified against employee facts
    for claim in ["1099 non-employee contractor",
                  "Non-Employee Compensation (1099-NEC)",
                  "Independent contractor (not an employee)"]:
        r = g.verify_classification_claim(claim, employee_facts)
        d = ClassificationGuard.to_diagnostic(r)
        assert r["verified"] and d.status.value == "VERIFIED" and d.is_authoritative
    
    # Missing facts and string facts are certified
    assert g.verify_classification_claim("1099", {})["verified"]
    assert g.verify_classification_claim("W-2", {"provides_tools": "no", "reimburses_expenses": "no",
                                                  "indefinite_relationship": "no"})["verified"]
    
    # Through the documented entry point
    TaxPreFlight().audit_transaction({"action": "hire",
                                      "worker_type": "1099 non-employee contractor",
                                      "worker_facts": employee_facts})
    # -> {'allowed': True, 'blocks': [], 'checks_run': ['worker_classification'], ...}
    

    Affected versions

    qwed-tax (PyPI) <= 0.2.0. The same normalisation code is present in 0.1.0.

    The npm package @qwed-ai/tax is not affected by this issue. Its TaxPreFlight.audit compares the claim as an exact string and rejects these phrasings.

    Patches

    Fixed in qwed-tax 0.2.1.

    Workarounds

    Until upgrading:

    • Pass only the exact values "W2" or "1099" as the claim, and reject any other claim text before calling the guard.
    • Validate that all three facts are present and are real booleans before calling verify_classification_claim.

    Fix

    • Accept a closed set of claim values with an explicit alias table. Any other text, or text that matches both classes or neither, returns a non-verified INVALID_CLAIM result.
    • Require provides_tools, reimburses_expenses and indefinite_relationship to be present and of type bool; otherwise fail closed.
    • Regression tests cover the claims above and missing / non-boolean facts on both the direct API and TaxPreFlight.