docs(01): create phase 1 foundation plans
Three plans covering Docker scaffold, SQLite schema, and .intunewin spike. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
---
|
||||
phase: 01-foundation
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- imptune/generators/__init__.py
|
||||
- imptune/generators/intunewin_builder.py
|
||||
- tests/test_intunewin.py
|
||||
autonomous: true
|
||||
requirements:
|
||||
- INFRA-02
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "A Python function produces a valid .intunewin file from a source directory and setup file name"
|
||||
- "The .intunewin file contains an outer ZIP with IntuneWinPackage/Contents/IntunePackage.intunewin and IntuneWinPackage/Metadata/Detection.xml"
|
||||
- "The encrypted blob uses the correct byte layout: HMAC-SHA256 (32 bytes) + IV (16 bytes) + AES-256-CBC ciphertext"
|
||||
- "Detection.xml contains correct EncryptionKey, MacKey, InitializationVector, Mac, FileDigest values that match the actual encryption"
|
||||
- "The inner ZIP uses DEFLATE compression and the outer ZIP uses STORED compression"
|
||||
artifacts:
|
||||
- path: "imptune/generators/intunewin_builder.py"
|
||||
provides: "Python-native .intunewin file assembler using pycryptodome"
|
||||
exports: ["build_intunewin"]
|
||||
min_lines: 60
|
||||
- path: "tests/test_intunewin.py"
|
||||
provides: "Byte-level validation tests for .intunewin format"
|
||||
min_lines: 80
|
||||
key_links:
|
||||
- from: "imptune/generators/intunewin_builder.py"
|
||||
to: "pycryptodome"
|
||||
via: "from Crypto.Cipher import AES"
|
||||
pattern: "Crypto\\.Cipher"
|
||||
- from: "imptune/generators/intunewin_builder.py"
|
||||
to: "zipfile"
|
||||
via: "stdlib zipfile for inner and outer ZIPs"
|
||||
pattern: "zipfile\\.ZipFile"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Implement the Python-native .intunewin file builder as a time-boxed spike. This module generates .intunewin packages using AES-256-CBC encryption with HMAC-SHA256, producing the exact byte layout Intune expects.
|
||||
|
||||
Purpose: Validate the highest-risk unknown in the project — can Python generate a .intunewin file that Intune accepts? This spike runs independently of the web app and produces a standalone generator module reused in Phase 5. Supports INFRA-02 (no external binary dependencies like IntuneWinAppUtil.exe).
|
||||
Output: A tested build_intunewin() function and comprehensive byte-level validation tests.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/01-foundation/01-CONTEXT.md
|
||||
@.planning/phases/01-foundation/01-RESEARCH.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Implement .intunewin builder with byte-level tests</name>
|
||||
<files>
|
||||
imptune/generators/__init__.py,
|
||||
imptune/generators/intunewin_builder.py,
|
||||
tests/test_intunewin.py
|
||||
</files>
|
||||
<behavior>
|
||||
- test_output_is_valid_zip: build_intunewin() output file is a valid ZIP archive
|
||||
- test_outer_zip_structure: outer ZIP contains exactly IntuneWinPackage/Contents/IntunePackage.intunewin and IntuneWinPackage/Metadata/Detection.xml
|
||||
- test_outer_zip_stored: outer ZIP entries use ZIP_STORED compression (no extra compression on encrypted content)
|
||||
- test_detection_xml_valid: Detection.xml is valid XML with ApplicationInfo root element in the correct namespace (http://schemas.microsoft.com/IntuneWin)
|
||||
- test_detection_xml_fields: Detection.xml contains Name, UnencryptedContentSize, FileName, SetupFile, and full EncryptionInfo with all 8 sub-elements (EncryptionKey, MacKey, InitializationVector, Mac, MacAlgorithm, ProfileIdentifier, FileDigest, FileDigestAlgorithm)
|
||||
- test_encrypted_blob_layout: the encrypted blob starts with 32 bytes (HMAC) + 16 bytes (IV) + remainder (ciphertext); total length = 48 + ciphertext length
|
||||
- test_iv_is_16_bytes: IV extracted from Detection.xml base64-decodes to exactly 16 bytes (NOT 32 — critical per RESEARCH.md)
|
||||
- test_encryption_key_is_32_bytes: EncryptionKey from Detection.xml base64-decodes to exactly 32 bytes
|
||||
- test_mac_key_is_32_bytes: MacKey from Detection.xml base64-decodes to exactly 32 bytes
|
||||
- test_hmac_matches: HMAC-SHA256 computed from MacKey over ciphertext matches the first 32 bytes of the blob AND the Mac value in Detection.xml
|
||||
- test_decryption_roundtrip: using EncryptionKey and IV from Detection.xml, decrypt the ciphertext, unpad, and verify the result is a valid DEFLATE-compressed ZIP containing the original source files
|
||||
- test_file_digest_matches: FileDigest in Detection.xml matches SHA256 of the decrypted plaintext ZIP
|
||||
- test_unencrypted_content_size: UnencryptedContentSize in Detection.xml matches the byte length of the decrypted plaintext ZIP
|
||||
- test_setup_file_in_detection_xml: SetupFile element matches the setup_file argument passed to build_intunewin
|
||||
</behavior>
|
||||
<action>
|
||||
**imptune/generators/intunewin_builder.py**:
|
||||
Implement build_intunewin(source_dir: str, setup_file: str, output_path: str) -> None following the skeleton from RESEARCH.md Pattern 3, with these specifics:
|
||||
|
||||
1. Create inner ZIP (DEFLATE compression) of all files in source_dir, preserving relative paths
|
||||
2. Generate random keys: aes_key = os.urandom(32), mac_key = os.urandom(32), iv = os.urandom(16) — IV MUST be 16 bytes per the critical correction in RESEARCH.md
|
||||
3. Encrypt with AES-256-CBC: cipher = AES.new(aes_key, AES.MODE_CBC, iv), ciphertext = cipher.encrypt(pad(plaintext, AES.block_size))
|
||||
4. Compute HMAC-SHA256 of ciphertext using mac_key
|
||||
5. Assemble encrypted blob: hmac_digest (32 bytes) + iv (16 bytes) + ciphertext
|
||||
6. Compute file_digest = SHA256 of plaintext (the inner ZIP bytes before encryption)
|
||||
7. Build Detection.xml with all required fields (see RESEARCH.md for exact schema). Use xml.etree.ElementTree for building and xml.dom.minidom for pretty printing. Set xmlns="http://schemas.microsoft.com/IntuneWin" on ApplicationInfo root.
|
||||
8. Build outer ZIP (STORED compression) with two entries: IntuneWinPackage/Contents/IntunePackage.intunewin (the encrypted blob) and IntuneWinPackage/Metadata/Detection.xml
|
||||
9. All base64 values in Detection.xml use standard base64 encoding (base64.b64encode)
|
||||
|
||||
**tests/test_intunewin.py**:
|
||||
- Create a tmp_path fixture with a small test source directory (2-3 small text files, one named "install.ps1")
|
||||
- Call build_intunewin(source_dir, "install.ps1", output_path) to generate the file
|
||||
- Implement all tests from the behavior list above
|
||||
- For the decryption roundtrip: extract EncryptionKey and IV from Detection.xml, use AES.new(key, AES.MODE_CBC, iv) to decrypt, unpad the result, verify it's a valid ZIP containing the original files
|
||||
- For HMAC verification: extract MacKey from Detection.xml, compute hmac.new(mac_key, ciphertext, hashlib.sha256).digest(), compare to first 32 bytes of blob AND to Mac value in Detection.xml
|
||||
|
||||
The tests serve as the format specification — if they pass, the byte layout is correct. The only remaining validation is a real Intune upload (manual, Phase 5 gate).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && pip install pycryptodome -q && python -m pytest tests/test_intunewin.py -x -v</automated>
|
||||
</verify>
|
||||
<done>
|
||||
- All 14 byte-level tests pass
|
||||
- IV is confirmed 16 bytes (not 32)
|
||||
- Decryption roundtrip succeeds: encrypt then decrypt recovers original files
|
||||
- HMAC verification succeeds: computed HMAC matches blob header and Detection.xml Mac field
|
||||
- Outer ZIP structure matches Intune's expected layout exactly
|
||||
- Detection.xml has correct namespace and all required fields
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- `python -m pytest tests/test_intunewin.py -x -v` — all 14 tests pass
|
||||
- `python -c "from imptune.generators.intunewin_builder import build_intunewin; print('Builder importable')"` — no import errors
|
||||
- The .intunewin file produced can be opened as a ZIP and inspected manually (outer structure visible)
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- build_intunewin() produces a file with the exact byte layout Intune expects
|
||||
- All crypto operations use correct key/IV sizes (32/32/16 bytes)
|
||||
- HMAC and decryption roundtrip verified programmatically
|
||||
- Detection.xml contains all 8 EncryptionInfo sub-elements with correct values
|
||||
- Module is standalone — no dependency on the web framework or database
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/01-foundation/01-03-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user