@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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/alpine/apkindexreader.d.ts +87 -0
- package/dist_ts/alpine/apkindexreader.js +193 -0
- package/dist_ts/arch/indexreader.d.ts +35 -0
- package/dist_ts/arch/indexreader.js +45 -0
- package/dist_ts/arch/reader.js +1 -1
- package/dist_ts/debian/indexbuilder.js +8 -23
- package/dist_ts/debian/indexreader.d.ts +107 -0
- package/dist_ts/debian/indexreader.js +274 -0
- package/dist_ts/debian/reader.js +1 -1
- package/dist_ts/debian/rules.d.ts +18 -0
- package/dist_ts/debian/rules.js +81 -2
- package/dist_ts/index.d.ts +14 -4
- package/dist_ts/index.js +9 -3
- package/dist_ts/readers/decompress.d.ts +1 -2
- package/dist_ts/readers/decompress.js +1 -1
- package/dist_ts/readers/document.d.ts +36 -0
- package/dist_ts/readers/document.js +110 -0
- package/dist_ts/rpm/repodata.d.ts +3 -2
- package/dist_ts/rpm/repodata.js +5 -4
- package/dist_ts/rpm/repodatareader.d.ts +173 -0
- package/dist_ts/rpm/repodatareader.js +414 -0
- package/dist_ts/rpm/xmlreader.d.ts +70 -0
- package/dist_ts/rpm/xmlreader.js +576 -0
- package/dist_ts/shared/errors.d.ts +12 -4
- package/dist_ts/shared/errors.js +1 -1
- package/dist_ts/shared/limits.d.ts +8 -1
- package/dist_ts/shared/limits.js +5 -2
- package/dist_ts/shared/types.d.ts +2 -0
- package/dist_ts/signing/verifiers.d.ts +43 -0
- package/dist_ts/signing/verifiers.js +81 -2
- package/package.json +1 -1
- package/readme.md +53 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/alpine/apkindexreader.ts +265 -0
- package/ts/arch/indexreader.ts +66 -0
- package/ts/arch/reader.ts +2 -1
- package/ts/debian/indexbuilder.ts +13 -23
- package/ts/debian/indexreader.ts +372 -0
- package/ts/debian/reader.ts +2 -1
- package/ts/debian/rules.ts +83 -1
- package/ts/index.ts +40 -2
- package/ts/readers/decompress.ts +1 -3
- package/ts/readers/document.ts +136 -0
- package/ts/rpm/repodata.ts +4 -3
- package/ts/rpm/repodatareader.ts +582 -0
- package/ts/rpm/xmlreader.ts +680 -0
- package/ts/shared/errors.ts +13 -4
- package/ts/shared/limits.ts +9 -1
- package/ts/shared/types.ts +3 -0
- 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
|
+
}
|
package/ts/rpm/repodata.ts
CHANGED
|
@@ -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
|
|
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 ||
|
|
132
|
+
const files = metadata.files.filter((file) => file.isExecutable || isRpmPrimaryPath(file.path));
|
|
132
133
|
return element(
|
|
133
134
|
'package',
|
|
134
135
|
[['type', 'rpm']],
|