@push.rocks/smartlinuxrepo 1.1.2 → 1.2.0

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.
Files changed (51) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/alpine/apkindexreader.d.ts +87 -0
  3. package/dist_ts/alpine/apkindexreader.js +193 -0
  4. package/dist_ts/arch/indexreader.d.ts +35 -0
  5. package/dist_ts/arch/indexreader.js +45 -0
  6. package/dist_ts/arch/reader.js +1 -1
  7. package/dist_ts/debian/indexbuilder.js +8 -23
  8. package/dist_ts/debian/indexreader.d.ts +107 -0
  9. package/dist_ts/debian/indexreader.js +274 -0
  10. package/dist_ts/debian/reader.js +1 -1
  11. package/dist_ts/debian/rules.d.ts +18 -0
  12. package/dist_ts/debian/rules.js +81 -2
  13. package/dist_ts/index.d.ts +14 -4
  14. package/dist_ts/index.js +9 -3
  15. package/dist_ts/readers/decompress.d.ts +1 -2
  16. package/dist_ts/readers/decompress.js +1 -1
  17. package/dist_ts/readers/document.d.ts +36 -0
  18. package/dist_ts/readers/document.js +110 -0
  19. package/dist_ts/rpm/repodata.d.ts +3 -2
  20. package/dist_ts/rpm/repodata.js +5 -4
  21. package/dist_ts/rpm/repodatareader.d.ts +173 -0
  22. package/dist_ts/rpm/repodatareader.js +414 -0
  23. package/dist_ts/rpm/xmlreader.d.ts +70 -0
  24. package/dist_ts/rpm/xmlreader.js +576 -0
  25. package/dist_ts/shared/errors.d.ts +12 -4
  26. package/dist_ts/shared/errors.js +1 -1
  27. package/dist_ts/shared/limits.d.ts +8 -1
  28. package/dist_ts/shared/limits.js +5 -2
  29. package/dist_ts/shared/types.d.ts +2 -0
  30. package/dist_ts/signing/verifiers.d.ts +43 -0
  31. package/dist_ts/signing/verifiers.js +81 -2
  32. package/package.json +1 -1
  33. package/readme.md +53 -2
  34. package/ts/00_commitinfo_data.ts +1 -1
  35. package/ts/alpine/apkindexreader.ts +265 -0
  36. package/ts/arch/indexreader.ts +66 -0
  37. package/ts/arch/reader.ts +2 -1
  38. package/ts/debian/indexbuilder.ts +13 -23
  39. package/ts/debian/indexreader.ts +372 -0
  40. package/ts/debian/reader.ts +2 -1
  41. package/ts/debian/rules.ts +83 -1
  42. package/ts/index.ts +40 -2
  43. package/ts/readers/decompress.ts +1 -3
  44. package/ts/readers/document.ts +136 -0
  45. package/ts/rpm/repodata.ts +4 -3
  46. package/ts/rpm/repodatareader.ts +582 -0
  47. package/ts/rpm/xmlreader.ts +680 -0
  48. package/ts/shared/errors.ts +13 -4
  49. package/ts/shared/limits.ts +9 -1
  50. package/ts/shared/types.ts +3 -0
  51. package/ts/signing/verifiers.ts +113 -1
@@ -0,0 +1,136 @@
1
+ import { LinuxRepoError, limitExceeded } from '../shared/errors.js';
2
+ import type { ILinuxPackageLimits } from '../shared/limits.js';
3
+ import type { TLinuxCompression } from '../shared/types.js';
4
+ import { assertBytes } from '../shared/validation.js';
5
+ import { BufferReader, READ_CHUNK_BYTES } from './bytes.js';
6
+ import { withDecompressingReader } from './decompress.js';
7
+
8
+ /**
9
+ * The shared parts of the index readers (1.2.0): decoding one index document
10
+ * into a buffer the reader owns, and the line lexer of the line formats
11
+ * (Debian `Packages` and `Release`, `APKINDEX`).
12
+ */
13
+
14
+ const COMPRESSIONS: ReadonlySet<string> = new Set<TLinuxCompression>(['none', 'gzip', 'xz', 'zstd']);
15
+ const LF = 0x0a;
16
+
17
+ /** Refuses a reader argument that is not an object (`invalid-input`, `input`). */
18
+ export function assertReaderInput(inputArg: unknown): asserts inputArg is Record<string, unknown> {
19
+ if (inputArg === null || typeof inputArg !== 'object') {
20
+ throw new LinuxRepoError('invalid-input', 'input must be an object', { field: 'input' });
21
+ }
22
+ }
23
+
24
+ /** Refuses a `bytes` argument that is not a `Uint8Array` (`invalid-input`, `bytes`). */
25
+ export function assertReaderBytes(valueArg: unknown): asserts valueArg is Uint8Array {
26
+ assertBytes(valueArg, 'bytes');
27
+ }
28
+
29
+ /** Refuses a compression that is not a `TLinuxCompression` (`invalid-input`, `compression`). */
30
+ export function assertLinuxCompression(valueArg: unknown): asserts valueArg is TLinuxCompression {
31
+ if (typeof valueArg !== 'string' || !COMPRESSIONS.has(valueArg)) {
32
+ throw new LinuxRepoError('invalid-input', "compression must be 'none', 'gzip', 'xz' or 'zstd'", {
33
+ field: 'compression',
34
+ value: typeof valueArg === 'string' ? valueArg : typeof valueArg,
35
+ });
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Decodes one index document into a new buffer the reader owns, through
41
+ * `withDecompressingReader` for every compression (`'none'` passes the input
42
+ * through and copies it), bounded by `maxIndexBytes`. The decoder's own
43
+ * mapping applies: a stream that ends early is `truncated`, corrupt data
44
+ * `malformed-archive`, output past the bound `limit-exceeded`
45
+ * (`maxIndexBytes`). The result never aliases `bytesArg`.
46
+ */
47
+ export async function readIndexDocument(
48
+ bytesArg: Uint8Array,
49
+ compressionArg: TLinuxCompression,
50
+ limitsArg: Readonly<ILinuxPackageLimits>,
51
+ ): Promise<Uint8Array> {
52
+ if (compressionArg === 'none' && bytesArg.length > limitsArg.maxIndexBytes) {
53
+ throw limitExceeded('maxIndexBytes', limitsArg.maxIndexBytes, 'index document');
54
+ }
55
+ const chunks: Uint8Array[] = [];
56
+ let total = 0;
57
+ await withDecompressingReader(
58
+ new BufferReader(bytesArg),
59
+ compressionArg,
60
+ { maxOutputBytes: limitsArg.maxIndexBytes, outputLimit: 'maxIndexBytes', maxZstdFrames: limitsArg.maxEntries },
61
+ async (readerArg) => {
62
+ for (let chunk = await readerArg.read(READ_CHUNK_BYTES); chunk !== null; chunk = await readerArg.read(READ_CHUNK_BYTES)) {
63
+ chunks.push(chunk);
64
+ total += chunk.length;
65
+ }
66
+ },
67
+ );
68
+ const decoded = new Uint8Array(total);
69
+ let offset = 0;
70
+ for (const chunk of chunks) {
71
+ decoded.set(chunk, offset);
72
+ offset += chunk.length;
73
+ }
74
+ return decoded;
75
+ }
76
+
77
+ /** One line of a line-format document: `[start, end)` without its LF, which sits at `end`. */
78
+ export interface ILine {
79
+ readonly start: number;
80
+ readonly end: number;
81
+ }
82
+
83
+ /**
84
+ * The common line lexer of the line formats: lines end in LF and the last
85
+ * line must have one too (`malformed-index`, `detail.value` the offset of
86
+ * the unterminated line); a line longer than `maxHeaderBytes` (without its
87
+ * LF) is `limit-exceeded`. CR is an ordinary byte; nothing is trimmed. An
88
+ * empty document has no lines.
89
+ */
90
+ export function* lines(bytesArg: Uint8Array, maxLineBytesArg: number, whatArg: string): Generator<ILine> {
91
+ let start = 0;
92
+ while (start < bytesArg.length) {
93
+ const end = bytesArg.indexOf(LF, start);
94
+ if (end < 0) {
95
+ if (bytesArg.length - start > maxLineBytesArg) {
96
+ throw limitExceeded('maxHeaderBytes', maxLineBytesArg, `${whatArg} line`);
97
+ }
98
+ throw new LinuxRepoError('malformed-index', `the last line of the ${whatArg} has no line feed`, {
99
+ field: whatArg,
100
+ value: start,
101
+ });
102
+ }
103
+ if (end - start > maxLineBytesArg) {
104
+ throw limitExceeded('maxHeaderBytes', maxLineBytesArg, `${whatArg} line`);
105
+ }
106
+ yield { start, end };
107
+ start = end + 1;
108
+ }
109
+ }
110
+
111
+ /** True when the line holds only spaces or tabs (at least one). */
112
+ export function isBlankLine(bytesArg: Uint8Array, lineArg: ILine): boolean {
113
+ if (lineArg.end === lineArg.start) {
114
+ return false;
115
+ }
116
+ for (let i = lineArg.start; i < lineArg.end; i++) {
117
+ if (bytesArg[i] !== 0x20 && bytesArg[i] !== 0x09) {
118
+ return false;
119
+ }
120
+ }
121
+ return true;
122
+ }
123
+
124
+ /** Counts one more unit of a document against `maxEntries`. */
125
+ export function countUnit(countArg: number, limitsArg: Readonly<ILinuxPackageLimits>, whatArg: string): void {
126
+ if (countArg > limitsArg.maxEntries) {
127
+ throw limitExceeded('maxEntries', limitsArg.maxEntries, `${whatArg} count`);
128
+ }
129
+ }
130
+
131
+ /** Refuses one unit larger than `maxMetadataBytes`. */
132
+ export function checkUnitBytes(lengthArg: number, limitsArg: Readonly<ILinuxPackageLimits>, whatArg: string): void {
133
+ if (lengthArg > limitsArg.maxMetadataBytes) {
134
+ throw limitExceeded('maxMetadataBytes', limitsArg.maxMetadataBytes, whatArg);
135
+ }
136
+ }
@@ -23,9 +23,10 @@ import { element, textElement, XML_HEADER } from './xml.js';
23
23
  * createrepo_c 1.2.6 `cr_is_primary` (`src/misc.h:111-119`): paths under
24
24
  * `/etc/`, exactly `/usr/lib/sendmail`, or containing `bin/` anywhere. It is
25
25
  * applied to every file entry, directories and ghosts included
26
- * (`src/xml_dump.c:206`).
26
+ * (`src/xml_dump.c:206`). `buildRpmIndex` lists such files in `primary`
27
+ * besides the executable ones, where Gitea lists only the executable ones.
27
28
  */
28
- export function isPrimaryPath(pathArg: string): boolean {
29
+ export function isRpmPrimaryPath(pathArg: string): boolean {
29
30
  return pathArg.startsWith('/etc/') || pathArg === '/usr/lib/sendmail' || pathArg.includes('bin/');
30
31
  }
31
32
 
@@ -128,7 +129,7 @@ export function renderRepomd(dataArg: IRepomdData[]): string {
128
129
 
129
130
  function renderPrimaryPackage(recordArg: IRpmPackageRecord): string {
130
131
  const metadata = recordArg.metadata;
131
- const files = metadata.files.filter((file) => file.isExecutable || isPrimaryPath(file.path));
132
+ const files = metadata.files.filter((file) => file.isExecutable || isRpmPrimaryPath(file.path));
132
133
  return element(
133
134
  'package',
134
135
  [['type', 'rpm']],