1*0e16a31dSJim Jagielski#************************************************************** 2*0e16a31dSJim Jagielski# 3*0e16a31dSJim Jagielski# Licensed to the Apache Software Foundation (ASF) under one 4*0e16a31dSJim Jagielski# or more contributor license agreements. See the NOTICE file 5*0e16a31dSJim Jagielski# distributed with this work for additional information 6*0e16a31dSJim Jagielski# regarding copyright ownership. The ASF licenses this file 7*0e16a31dSJim Jagielski# to you under the Apache License, Version 2.0 (the 8*0e16a31dSJim Jagielski# "License"); you may not use this file except in compliance 9*0e16a31dSJim Jagielski# with the License. You may obtain a copy of the License at 10*0e16a31dSJim Jagielski# 11*0e16a31dSJim Jagielski# http://www.apache.org/licenses/LICENSE-2.0 12*0e16a31dSJim Jagielski# 13*0e16a31dSJim Jagielski# Unless required by applicable law or agreed to in writing, 14*0e16a31dSJim Jagielski# software distributed under the License is distributed on an 15*0e16a31dSJim Jagielski# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*0e16a31dSJim Jagielski# KIND, either express or implied. See the License for the 17*0e16a31dSJim Jagielski# specific language governing permissions and limitations 18*0e16a31dSJim Jagielski# under the License. 19*0e16a31dSJim Jagielski# 20*0e16a31dSJim Jagielski#************************************************************** 21*0e16a31dSJim Jagielski 22*0e16a31dSJim Jagielski# usage: make-pkg-controls.py -- (re)generate pkgcheck.py's negative-control packages in fixtures/control-pkg/ 23*0e16a31dSJim Jagielski# .expected.json maps each package to the (level, rule) pairs pkgcheck.py must report, exactly. 24*0e16a31dSJim Jagielskiimport json, os, shutil, warnings, zipfile 25*0e16a31dSJim Jagielski 26*0e16a31dSJim JagielskiC = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures", "control-pkg") 27*0e16a31dSJim JagielskiMIME = "application/vnd.oasis.opendocument.text" 28*0e16a31dSJim JagielskiS, Z = zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED 29*0e16a31dSJim JagielskiSIG = ('<?xml version="1.0" encoding="UTF-8"?>\n<dsig:document-signatures xmlns:dsig="urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0" ' 30*0e16a31dSJim Jagielski 'dsig:version="%s" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:Signature Id="s1"/></dsig:document-signatures>\n') 31*0e16a31dSJim JagielskiENC = ('<manifest:encryption-data manifest:checksum-type="SHA1/1K" manifest:checksum="AAAA">' 32*0e16a31dSJim Jagielski '<manifest:algorithm manifest:algorithm-name="Blowfish CFB" manifest:initialisation-vector="AAAA"/>' 33*0e16a31dSJim Jagielski '<manifest:key-derivation manifest:key-derivation-name="PBKDF2" manifest:key-size="16" manifest:iteration-count="1024" manifest:salt="AAAA"/>' 34*0e16a31dSJim Jagielski '</manifest:encryption-data>') 35*0e16a31dSJim Jagielski 36*0e16a31dSJim Jagielski 37*0e16a31dSJim Jagielskidef entry(path, mt="text/xml", inner="", **attrs): 38*0e16a31dSJim Jagielski a = "".join(' manifest:%s="%s"' % kv for kv in attrs.items()) 39*0e16a31dSJim Jagielski return '<manifest:file-entry manifest:full-path="%s" manifest:media-type="%s"%s>%s</manifest:file-entry>' % (path, mt, a, inner) 40*0e16a31dSJim Jagielski 41*0e16a31dSJim Jagielski 42*0e16a31dSJim Jagielskidef manifest(*entries, root="manifest:manifest"): 43*0e16a31dSJim Jagielski return ('<?xml version="1.0" encoding="UTF-8"?>\n<%s xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">%s</%s>\n' 44*0e16a31dSJim Jagielski % (root, "".join(entries), root)) 45*0e16a31dSJim Jagielski 46*0e16a31dSJim Jagielski 47*0e16a31dSJim JagielskiROOT = entry("/", MIME, version="1.2") 48*0e16a31dSJim JagielskiCONTENT = entry("content.xml") 49*0e16a31dSJim JagielskiGOOD_MANI = manifest(ROOT, CONTENT) 50*0e16a31dSJim JagielskiEXPECTED = {} 51*0e16a31dSJim Jagielski 52*0e16a31dSJim Jagielski 53*0e16a31dSJim Jagielskidef build(name, files, expect=()): 54*0e16a31dSJim Jagielski """files: (zip name, data, method[, extra]) in physical order.""" 55*0e16a31dSJim Jagielski with zipfile.ZipFile(os.path.join(C, name + ".odt"), "w") as z, warnings.catch_warnings(): 56*0e16a31dSJim Jagielski warnings.simplefilter("ignore") 57*0e16a31dSJim Jagielski for f in files: 58*0e16a31dSJim Jagielski zi = zipfile.ZipInfo(f[0]) 59*0e16a31dSJim Jagielski if len(f) > 3: 60*0e16a31dSJim Jagielski zi.extra = f[3] 61*0e16a31dSJim Jagielski z.writestr(zi, f[1], compress_type=f[2]) 62*0e16a31dSJim Jagielski EXPECTED[name + ".odt"] = sorted(expect) 63*0e16a31dSJim Jagielski 64*0e16a31dSJim Jagielski 65*0e16a31dSJim Jagielskidef std(mani=GOOD_MANI, mime=MIME, extra=()): 66*0e16a31dSJim Jagielski return [("mimetype", mime, S), ("content.xml", "<c/>", Z)] + list(extra) + [("META-INF/manifest.xml", mani, Z)] 67*0e16a31dSJim Jagielski 68*0e16a31dSJim Jagielski 69*0e16a31dSJim Jagielskishutil.rmtree(C, ignore_errors=True) 70*0e16a31dSJim Jagielskios.makedirs(C) 71*0e16a31dSJim Jagielskibuild("good", std()) 72*0e16a31dSJim Jagielskisub = [("Object 1/content.xml", "<c/>", Z), ("Pictures/a.png", "x", S), ("secret.xml", "0123456789", S), 73*0e16a31dSJim Jagielski ("META-INF/documentsignatures.xml", SIG % "1.2", Z)] 74*0e16a31dSJim Jagielskibuild("good-full", std(manifest(ROOT, CONTENT, entry("Object 1/", MIME, version="1.2"), entry("Object 1/content.xml"), 75*0e16a31dSJim Jagielski entry("Pictures/a.png", "image/png"), entry("secret.xml", inner=ENC, size="10")), extra=sub)) 76*0e16a31dSJim Jagielskibuild("mime-deflated", [("mimetype", MIME, Z), ("content.xml", "<c/>", Z), ("META-INF/manifest.xml", GOOD_MANI, Z)], [("shall", "mimetype-compressed")]) 77*0e16a31dSJim Jagielskibuild("mime-extra", [("mimetype", MIME, S, b"\xfe\xca\x00\x00"), ("content.xml", "<c/>", Z), ("META-INF/manifest.xml", GOOD_MANI, Z)], [("shall", "mimetype-extra")]) 78*0e16a31dSJim Jagielskibuild("mime-notfirst", [("content.xml", "<c/>", Z), ("mimetype", MIME, S), ("META-INF/manifest.xml", GOOD_MANI, Z)], [("shall", "mimetype-first")]) 79*0e16a31dSJim Jagielskibuild("mime-mismatch", std(mime="application/x-bogus"), [("shall", "mimetype-mismatch")]) 80*0e16a31dSJim Jagielskibuild("mime-nonascii", std(mime=MIME + "é"), [("shall", "mimetype-ascii")]) 81*0e16a31dSJim Jagielskibuild("mime-missing", std()[1:], [("should", "mimetype-missing"), ("shall", "mimetype-missing")]) 82*0e16a31dSJim Jagielskibuild("mime-missing-noroot", std(manifest(CONTENT))[1:], [("should", "mimetype-missing")]) 83*0e16a31dSJim Jagielskibuild("no-root-entry", std(manifest(CONTENT)), [("shall", "manifest-root-entry")]) 84*0e16a31dSJim Jagielskibuild("uncovered", std(extra=[("extra.xml", "<e/>", Z)]), [("shall", "manifest-coverage")]) 85*0e16a31dSJim Jagielskibuild("dup-entry", std(manifest(ROOT, CONTENT, CONTENT)), [("shall", "manifest-duplicate")]) 86*0e16a31dSJim Jagielskibuild("self-entry", std(manifest(ROOT, CONTENT, entry("mimetype", "text/plain"))), [("shall", "manifest-self")]) 87*0e16a31dSJim Jagielskibuild("dangling", std(manifest(ROOT, CONTENT, entry("gone.xml"))), [("should", "manifest-dangling")]) 88*0e16a31dSJim Jagielskibuild("dir-entry-manifest", std(manifest(ROOT, CONTENT, entry("Configurations2/", "application/vnd.sun.xml.ui.configuration"))), [("should", "manifest-dir-entry")]) 89*0e16a31dSJim Jagielskibuild("stray-meta-inf", std(extra=[("META-INF/foo.xml", "<f/>", Z)]), [("shall", "meta-inf-file")]) 90*0e16a31dSJim Jagielskibuild("no-manifest", [("mimetype", MIME, S), ("content.xml", "<c/>", Z)], [("shall", "manifest-missing")]) 91*0e16a31dSJim Jagielskibuild("manifest-malformed", std("<manifest:manifest"), [("shall", "manifest-malformed")]) 92*0e16a31dSJim Jagielskibuild("manifest-wrongroot", std(manifest(root="manifest:other")), [("shall", "manifest-root")]) 93*0e16a31dSJim Jagielskibuild("zip-bzip2", std(extra=[("b.xml", "<b/>", zipfile.ZIP_BZIP2)], mani=manifest(ROOT, CONTENT, entry("b.xml"))), [("shall", "zip-method")]) 94*0e16a31dSJim Jagielskibuild("zip-dup-name", std(extra=[("content.xml", "<d/>", Z)]), [("shall", "zip-duplicate")]) 95*0e16a31dSJim Jagielskibuild("zip-dir-entry", std(extra=[("Pictures/", "", S)]), [("should", "zip-dir-entry")]) 96*0e16a31dSJim Jagielskibuild("enc-nosize", std(manifest(ROOT, CONTENT, entry("secret.xml", inner=ENC)), extra=[("secret.xml", "0123456789", S)]), [("shall", "encrypted-size")]) 97*0e16a31dSJim Jagielskibuild("enc-deflated", std(manifest(ROOT, CONTENT, entry("secret.xml", inner=ENC, size="10")), extra=[("secret.xml", "0123456789", Z)]), [("shall", "encrypted-method")]) 98*0e16a31dSJim Jagielskibuild("sig-malformed", std(extra=[("META-INF/documentsignatures.xml", "<dsig", Z)]), [("shall", "signature-malformed")]) 99*0e16a31dSJim Jagielskibuild("sig-wrongroot", std(extra=[("META-INF/documentsignatures.xml", "<a/>", Z)]), [("shall", "signature-root")]) 100*0e16a31dSJim Jagielskibuild("sig-badversion", std(extra=[("META-INF/documentsignatures.xml", SIG % "1.1", Z)]), [("shall", "signature-schema")]) 101*0e16a31dSJim Jagielskijson.dump(EXPECTED, open(os.path.join(C, ".expected.json"), "w"), indent=1) 102