Skip to content

Hardened feature for importing answers from a JSON file#1027

Draft
nonprofittechy wants to merge 12 commits intomainfrom
answer-set-import-review
Draft

Hardened feature for importing answers from a JSON file#1027
nonprofittechy wants to merge 12 commits intomainfrom
answer-set-import-review

Conversation

@nonprofittechy
Copy link
Member

@nonprofittechy nonprofittechy commented Mar 10, 2026

Hardens the answer import functionality significantly, although I'd still caution authors that they might not need to turn this on for normal use.

fix #481

  1. Expands the explicit deny list significantly to cover more built-in variables that we do not want to shadow
  2. Adds an explicit set of allowed classes to load in; filters out DAFile to better prevent disclosure of system files. For now, defaults to just ALIndividual, ALPeopleList, and safe Docassemble built-in classes.
  3. Explicitly filter out any variables that are not mentioned in the interview's context already, using same method playground uses to check what variables an interview mentions. This may be too strict, but since nobody uses JSON answers yet, I think it will probably be OK. Workaround would be for author to explicitly add a code block that sets a dummy value for these variables, so easy to workaround from author's PoV.
  4. Checks for dangerous patterns like setting __dunder__ methods.
  5. Limits on stack depth and JSON string size.

An interactive interview to test with:

modules:
  - .sessions
---
mandatory: True
code: |
  import json
  
  minimal_var = "minimal"

  payload = {
    "minimal_var": "I am allowed",
    "mako_var": "I am in mako",
    "field_var": "I am in field",
    "evil_var": "I am evil",
    "log": "I love logs",
    "menu_items": [
      {"hello": "Hello"}
    ],
    "url_args": {
      "hello": 123
    }
  }

  load_interview_json(json.dumps(payload))
  report = get_last_import_report()
  accepted = list(report.get("accepted", {}))
  if not accepted and type(report.get("accepted")) is dict:
      accepted = list(report["accepted"].keys())

  rejected = [r['path'] for r in report.get("rejected", [])]

  from docassemble.base.functions import current_context
  all_vars = sorted(list(_get_target_interview_variables(current_context().filename)))

  test_finished = True
---
question: "A simple page"
fields:
  - "Foo": field_var
---
question: "A simple mako"
subquestion: |
  ${ mako_var }
---
mandatory: True
question: Test Results
subquestion: |

  ${ url_args.get('hello') }
  <pre id="test_results">
  {
    "accepted": ${ json.dumps(accepted) },
    "rejected": ${ json.dumps(rejected) },
    "all_vars": ${ json.dumps(all_vars) }
  }
  </pre>

@nonprofittechy nonprofittechy changed the title Starting place-hardened security for imports Hardened feature for importing answers from a JSON file Mar 10, 2026
@nonprofittechy
Copy link
Member Author

@ll-jgraszer your comments welcome too

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the “import answers from JSON” feature in AssemblyLine by adding structural validation, strict variable/class filtering, and user-facing reporting to reduce the risk of malicious or malformed payloads (per #481).

Changes:

  • Added defensive JSON parsing and sanitization in sessions.load_interview_json() (limits, protected variable blocking, object/class allowlisting, class remapping, dunder/key pattern checks).
  • Added an import report API (get_last_import_report) and wired the saved-sessions interview flow to display warnings/rejections to the user.
  • Added unit tests and sample JSON fixtures covering adversarial cases and object graph handling; documented configuration options in the README.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
docassemble/AssemblyLine/sessions.py Core hardening: parse limits, variable/class allowlists, object envelope validation, and import reporting.
docassemble/AssemblyLine/test_sessions.py New unit tests covering malformed JSON, limit enforcement, protected vars, object allowlisting/remaps, and adversarial payloads.
docassemble/AssemblyLine/data/sources/answer_set_import_samples/*.json Test fixtures for safe and malicious import scenarios (object graphs, dunder/internal keys, malformed JSON).
docassemble/AssemblyLine/data/questions/al_visual.yml Adds an interview-level gate (al_allow_answer_set_imports) to the “Import answer set” menu item visibility logic.
docassemble/AssemblyLine/data/questions/al_settings.yml Defines al_allow_answer_set_imports (default True) for interview-level control of imports.
docassemble/AssemblyLine/data/questions/al_saved_sessions.yml Uses is_valid_json() for validation; calls get_last_import_report() and displays warnings/rejections.
docassemble/AssemblyLine/data/questions/al_document.yml Minor string quoting consistency changes.
README.md Adds configuration documentation for the hardened JSON import behavior and limits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhance security of import from JSON feature

3 participants