@runeditrun/rer 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/cli/README.md +21 -0
- package/cli/container.mjs +11 -10
- package/cli/contract.mjs +2 -2
- package/cli/demo-host.mjs +47 -0
- package/cli/demo.mjs +47 -0
- package/cli/dependencies.mjs +26 -4
- package/cli/deploy-command.mjs +94 -0
- package/cli/deploy-inputs.mjs +22 -0
- package/cli/deploy-observation.mjs +28 -0
- package/cli/deploy-owner.mjs +52 -37
- package/cli/deploy-readiness.mjs +16 -0
- package/cli/deploy.mjs +20 -10
- package/cli/detached-server.mjs +1 -1
- package/cli/export.mjs +1 -1
- package/cli/hosted-output.mjs +2 -1
- package/cli/lifecycle.mjs +20 -7
- package/cli/local.mjs +7 -0
- package/cli/main.mjs +1 -1
- package/cli/managed-server.mjs +27 -18
- package/cli/parse.mjs +7 -2
- package/cli/pnpm-probe.mjs +4 -1
- package/cli/portability.mjs +4 -1
- package/cli/scheduled.mjs +2 -2
- package/cli/setup-input.mjs +1 -1
- package/global/CHANGELOG.md +46 -0
- package/global/README.md +9 -2
- package/global/launch/capacity.mjs +1 -1
- package/global/launch/facts.mjs +12 -6
- package/global/launch/install-pinned.mjs +26 -2
- package/global/launch/prepare.mjs +20 -8
- package/global/launch/state.mjs +54 -1
- package/global/main.mjs +18 -20
- package/global/update/command.mjs +2 -1
- package/modules/container/build.mjs +4 -2
- package/modules/container/host.mjs +1 -1
- package/modules/container/ingress.mjs +4 -1
- package/modules/container/migrations.mjs +2 -1
- package/modules/container/worker-engine.mjs +3 -1
- package/modules/demo-host/README.md +30 -0
- package/modules/demo-host/capacity.mjs +19 -0
- package/modules/demo-host/claim.mjs +21 -0
- package/modules/demo-host/container.mjs +74 -0
- package/modules/demo-host/pages.mjs +56 -0
- package/modules/demo-host/plan.mjs +52 -0
- package/modules/demo-host/worker.mjs +72 -0
- package/modules/deploy-kit/README.md +27 -0
- package/modules/deploy-kit/env-file.mjs +2 -1
- package/modules/deploy-kit/health-request.mjs +7 -2
- package/modules/deploy-kit/migration-ledger.mjs +38 -1
- package/modules/deploy-kit/migrations.mjs +38 -2
- package/modules/deploy-kit/owner-cli.mjs +30 -9
- package/modules/deploy-kit/owner-remove.mjs +5 -2
- package/modules/deploy-kit/owner-secret.mjs +67 -22
- package/modules/deploy-kit/owner.mjs +71 -24
- package/modules/deploy-kit/prepare.mjs +3 -1
- package/modules/deploy-kit/provider.mjs +3 -0
- package/modules/erasure/README.md +2 -3
- package/modules/operator-auth/accounts/settings-page.mjs +8 -3
- package/modules/operator-auth/csp.mjs +26 -0
- package/modules/operator-auth/dev/badge.mjs +1 -22
- package/modules/operator-auth/page.mjs +2 -2
- package/modules/operator-auth/routes.mjs +1 -1
- package/modules/portability/README.md +9 -0
- package/modules/portability/bridge.mjs +4 -1
- package/modules/portability/catalogue.mjs +9 -2
- package/modules/portability/index.d.mts +4 -0
- package/modules/portability/index.mjs +1 -0
- package/modules/portability/lines.mjs +3 -1
- package/modules/release/produce.mjs +1 -1
- package/modules/release/source.mjs +5 -2
- package/onboarding/adapter-labels.mjs +1 -0
- package/onboarding/adapter.mjs +9 -4
- package/onboarding/dependency-environment.mjs +33 -0
- package/onboarding/local/owner.mjs +1 -1
- package/onboarding/operator-installer.mjs +10 -4
- package/onboarding/redaction.mjs +22 -3
- package/package.json +6 -1
- package/scripts/publish-check/README.md +22 -1
- package/scripts/publish-check/c2pa.py +121 -0
- package/scripts/publish-check/content_identities.py +3 -1
- package/scripts/publish-check/controller_metadata.py +31 -0
- package/scripts/publish-check/history_scan.py +13 -3
- package/scripts/publish-check/media.py +283 -0
- package/scripts/publish-check/png_text.py +34 -0
- package/scripts/publish-check/public_hygiene.py +39 -4
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Separate uncompressed PNG text fields from chunk framing and CRC bytes."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def text_chunks(data):
|
|
5
|
+
# Detect the container by bytes, never by a publication path or extension.
|
|
6
|
+
if not data.startswith(b'\x89PNG\r\n\x1a\n'):
|
|
7
|
+
return
|
|
8
|
+
offset = 8
|
|
9
|
+
while offset + 12 <= len(data):
|
|
10
|
+
start = offset
|
|
11
|
+
length = int.from_bytes(data[offset:offset + 4], 'big')
|
|
12
|
+
end = offset + 8 + length
|
|
13
|
+
if end + 4 > len(data):
|
|
14
|
+
return
|
|
15
|
+
kind = data[offset + 4:offset + 8]
|
|
16
|
+
payload = data[offset + 8:end]
|
|
17
|
+
offset = end + 4
|
|
18
|
+
# Checksums do not confer an exemption: inspect a framed text field
|
|
19
|
+
# even if its CRC is wrong. Non-text chunks keep raw-byte inspection.
|
|
20
|
+
if kind == b'tEXt':
|
|
21
|
+
fields = payload.split(b'\0', 1)
|
|
22
|
+
if len(fields) == 2:
|
|
23
|
+
yield start, offset, [field.decode('latin-1').encode('utf-8') for field in fields]
|
|
24
|
+
elif kind == b'iTXt':
|
|
25
|
+
fields = payload.split(b'\0', 1)
|
|
26
|
+
if len(fields) != 2 or len(fields[1]) < 2:
|
|
27
|
+
continue
|
|
28
|
+
keyword, body = fields
|
|
29
|
+
fields = body[2:].split(b'\0', 2)
|
|
30
|
+
# The compression method is ignored when the flag is zero.
|
|
31
|
+
if body[0] == 0 and len(fields) == 3:
|
|
32
|
+
yield start, offset, [keyword.decode('latin-1').encode('utf-8'), *fields]
|
|
33
|
+
if kind == b'IEND':
|
|
34
|
+
return
|
|
@@ -8,6 +8,9 @@ from provenance import provenance_text
|
|
|
8
8
|
from hygiene_config import EMAIL, LOCAL_PATH, public_email
|
|
9
9
|
from font_licence import without_required_notices
|
|
10
10
|
from trust_bundle import anchor_values
|
|
11
|
+
from png_text import text_chunks
|
|
12
|
+
from media import sample_spans
|
|
13
|
+
from c2pa import certificate_spans
|
|
11
14
|
URI_AUTHORITY = re.compile(r'''(?i)(?a:(?P<scheme>[a-z][a-z0-9+.-]*:))?//(?P<authority>[^/?#\s<>"']+)''')
|
|
12
15
|
# Match raw, complete commit trailer lines; decoded or embedded prose cannot
|
|
13
16
|
# acquire trailer privileges. Only the address span is omitted from email checks.
|
|
@@ -47,7 +50,7 @@ def email_userinfo(match):
|
|
|
47
50
|
def private_emails(text, config):
|
|
48
51
|
# URL paths, queries and fragments remain visible to email inspection.
|
|
49
52
|
email_text = URI_AUTHORITY.sub(email_userinfo, without_required_notices(text))
|
|
50
|
-
email_text = re.sub(r'(?i)\b(?a:git@github\.com:)(?=[A-Za-z0-9])', '', email_text)
|
|
53
|
+
email_text = re.sub(r'(?i)\b(?a:git@github\.com:)(?=[A-Za-z0-9]|\$\{[A-Za-z_$][A-Za-z0-9_.$]*\})', '', email_text)
|
|
51
54
|
# A package manager's patch filename joins the name and version with an at sign.
|
|
52
55
|
version = r'(?:v?[0-9]+(?:\.[0-9]+)*(?:-[A-Za-z0-9-]+)?|[a-f0-9]{40})(?:\.patch)?(?![A-Za-z0-9_.-])'
|
|
53
56
|
email_text = re.sub(r'(?m)^\s*-?\s*uses:\s*[\"\']?[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+@[^\s\"\']+', '', email_text)
|
|
@@ -92,11 +95,43 @@ def certificate_metadata(data, bundle, emails, config):
|
|
|
92
95
|
return not Counter(emails) - inside
|
|
93
96
|
|
|
94
97
|
|
|
98
|
+
def without_spans(data, spans):
|
|
99
|
+
# Each span becomes one space: text on either side stays apart, and no
|
|
100
|
+
# long blank run is left for the line-anchored patterns to rescan.
|
|
101
|
+
kept, offset = [], 0
|
|
102
|
+
for start, end in sorted(spans):
|
|
103
|
+
kept.append(data[offset:start])
|
|
104
|
+
offset = max(offset, end)
|
|
105
|
+
kept.append(data[offset:])
|
|
106
|
+
return b' '.join(kept)
|
|
107
|
+
|
|
108
|
+
|
|
95
109
|
def inspect(data, path, config, resolver, *, commit_message=False):
|
|
96
|
-
|
|
110
|
+
chunks = list(text_chunks(data))
|
|
111
|
+
# Image and video samples are not text: they can spell an address or a
|
|
112
|
+
# path by chance. Comments, XMP, tags and other metadata remain text.
|
|
113
|
+
spans = sample_spans(data) or []
|
|
114
|
+
# Names in a C2PA manifest's signing certificates are certificate metadata.
|
|
115
|
+
spans += certificate_spans(data)
|
|
116
|
+
rules, provenance = set(), False
|
|
117
|
+
for start, end, fields in chunks:
|
|
118
|
+
spans.append((start, end))
|
|
119
|
+
for field in fields:
|
|
120
|
+
# A metadata value is text, not a standalone source manifest or
|
|
121
|
+
# trust bundle, even when the containing image has a JSON name.
|
|
122
|
+
field_rules, field_provenance = inspect_text(field, '@png-text', config, resolver, binary_content=False)
|
|
123
|
+
rules.update(field_rules)
|
|
124
|
+
provenance |= field_provenance
|
|
125
|
+
# Keep the original binary classification for content outside text chunks.
|
|
126
|
+
# CRC-adjacent bytes must not also create a synthetic raw mailbox match.
|
|
127
|
+
raw_rules, raw_provenance = inspect_text(without_spans(data, spans), path, config, resolver,
|
|
128
|
+
commit_message=commit_message, binary_content=binary(data))
|
|
129
|
+
rules.update(raw_rules)
|
|
130
|
+
provenance |= raw_provenance
|
|
131
|
+
return rules, provenance
|
|
97
132
|
|
|
98
133
|
|
|
99
|
-
def inspect_text(data, path, config, resolver, *, commit_message=False):
|
|
134
|
+
def inspect_text(data, path, config, resolver, *, commit_message=False, binary_content=None):
|
|
100
135
|
# NUL removal covers UTF-16/32 ASCII content, matching the secret scanner.
|
|
101
136
|
text = decoded_text(data)
|
|
102
137
|
rules = set()
|
|
@@ -111,7 +146,7 @@ def inspect_text(data, path, config, resolver, *, commit_message=False):
|
|
|
111
146
|
if emails and not (bundle and certificate_metadata(data, bundle, emails, config)):
|
|
112
147
|
# Inside binary data a mailbox is judged by its own shape: a host
|
|
113
148
|
# name with mixed-case letters inside a label is not one.
|
|
114
|
-
if not binary(data) or any(stored_host(email) for email in emails):
|
|
149
|
+
if not (binary(data) if binary_content is None else binary_content) or any(stored_host(email) for email in emails):
|
|
115
150
|
rules.add('hygiene.email')
|
|
116
151
|
ref_text, provenance = provenance_text(text, path, config.get('declaredSources', frozenset()))
|
|
117
152
|
stated, ambiguous = reference_sets(ref_text, path, config.get('localPaths', frozenset()), config.get('projectIdentity'))
|