securemark 0.289.6 → 0.290.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.
@@ -50,16 +50,23 @@ export const media: MediaParser = lazy(() => constraint(State.media, false, vali
50
50
  const INSECURE_URI = params.shift()!;
51
51
  assert(INSECURE_URI === INSECURE_URI.trim());
52
52
  assert(!INSECURE_URI.match(/\s/));
53
- const url = new ReadonlyURL(
54
- resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
55
- context.host?.href || location.href);
53
+ // altが空だとエラーが見えないため埋める。
54
+ text ||= INSECURE_URI;
55
+ let uri: ReadonlyURL | undefined;
56
+ try {
57
+ uri = new ReadonlyURL(
58
+ resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
59
+ context.host?.href || location.href);
60
+ }
61
+ catch {
62
+ }
56
63
  let cache: HTMLElement | undefined;
57
64
  const el = undefined
58
- || (cache = context.caches?.media?.get(url.href)?.cloneNode(true))
59
- || html('img', { class: 'media', 'data-src': url.source, alt: text });
65
+ || uri && (cache = context.caches?.media?.get(uri.href)?.cloneNode(true))
66
+ || html('img', { class: 'media', 'data-src': uri?.source });
60
67
  assert(!el.matches('.invalid'));
61
- cache?.hasAttribute('alt') && cache.setAttribute('alt', text);
62
- if (!sanitize(el, url, text)) return [[el], rest];
68
+ el.setAttribute('alt', text);
69
+ if (!sanitize(el, uri, text)) return [[el], rest];
63
70
  assert(!el.matches('.invalid'));
64
71
  define(el, attributes('media', push([], el.classList), optspec, params));
65
72
  assert(el.matches('img') || !el.matches('.invalid'));
@@ -97,34 +104,36 @@ const option: MediaParser.ParameterParser.OptionParser = lazy(() => union([
97
104
  linkoption,
98
105
  ]));
99
106
 
100
- function sanitize(target: HTMLElement, uri: ReadonlyURL, alt: string): boolean {
107
+ function sanitize(target: HTMLElement, uri: ReadonlyURL | undefined, alt: string): boolean {
101
108
  assert(target.tagName === 'IMG');
102
109
  assert(!target.matches('.invalid'));
103
- switch (uri.protocol) {
110
+ let type: string;
111
+ let message: string;
112
+ if (!alt.includes(Command.Error)) switch (uri?.protocol) {
113
+ case undefined:
114
+ type = 'argument';
115
+ message = 'Invalid URI';
116
+ break;
104
117
  case 'http:':
105
118
  case 'https:':
106
119
  assert(uri.host);
107
- if (/\/\.\.?(?:\/|$)/.test('/' + uri.source.slice(0, uri.source.search(/[?#]|$/)))) {
108
- define(target, {
109
- class: 'invalid',
110
- ...invalid('media', 'argument',
111
- 'Dot-segments cannot be used in media paths; use subresource paths instead')
112
- });
113
- return false;
114
- }
120
+ if (!/\/\.\.?(?:\/|$)/.test('/' + uri.source.slice(0, uri.source.search(/[?#]|$/)))) return true;
121
+ type = 'argument';
122
+ message = 'Dot-segments cannot be used in media paths; use subresource paths instead';
115
123
  break;
116
124
  default:
117
- define(target, { class: 'invalid', ...invalid('media', 'argument', 'Invalid protocol') });
118
- return false;
125
+ type = 'argument';
126
+ message = 'Invalid protocol';
119
127
  }
120
- if (alt.includes(Command.Error)) {
121
- define(target, {
122
- class: 'invalid',
123
- alt: target.getAttribute('alt')?.replace(CmdRegExp.Error, ''),
124
- ...invalid('media', 'argument',
125
- `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}"`)
126
- });
127
- return false;
128
+ else {
129
+ target.setAttribute('alt', alt.replace(CmdRegExp.Error, ''));
130
+ type = 'argument';
131
+ message = `Invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}"`;
128
132
  }
129
- return true;
133
+ define(target, {
134
+ 'data-src': null,
135
+ class: 'invalid',
136
+ ...invalid('link', type, message),
137
+ });
138
+ return false;
130
139
  }
@@ -16,8 +16,8 @@ describe('Unit: parser/inline/shortmedia', () => {
16
16
  });
17
17
 
18
18
  it('basic', () => {
19
- assert.deepStrictEqual(inspect(parser('!http://host')), [['<a href="http://host" target="_blank"><img class="media" data-src="http://host" alt=""></a>'], '']);
20
- assert.deepStrictEqual(inspect(parser('!https://host')), [['<a href="https://host" target="_blank"><img class="media" data-src="https://host" alt=""></a>'], '']);
19
+ assert.deepStrictEqual(inspect(parser('!http://host')), [['<a href="http://host" target="_blank"><img class="media" data-src="http://host" alt="http://host"></a>'], '']);
20
+ assert.deepStrictEqual(inspect(parser('!https://host')), [['<a href="https://host" target="_blank"><img class="media" data-src="https://host" alt="https://host"></a>'], '']);
21
21
  });
22
22
 
23
23
  });
@@ -12,8 +12,6 @@ describe('Unit: parser/inline/template', () => {
12
12
  assert.deepStrictEqual(inspect(parser('{}')), undefined);
13
13
  assert.deepStrictEqual(inspect(parser('{{')), undefined);
14
14
  assert.deepStrictEqual(inspect(parser('{{\\}}')), undefined);
15
- assert.deepStrictEqual(inspect(parser('{{\n}}')), undefined);
16
- assert.deepStrictEqual(inspect(parser('{{\\\n}}')), undefined);
17
15
  assert.deepStrictEqual(inspect(parser('{{a}b}')), undefined);
18
16
  assert.deepStrictEqual(inspect(parser('{{{a}}')), undefined);
19
17
  assert.deepStrictEqual(inspect(parser(' {{}}')), undefined);
@@ -24,6 +22,8 @@ describe('Unit: parser/inline/template', () => {
24
22
  assert.deepStrictEqual(inspect(parser('{{}}}')), [['<span class="template">{{}}</span>'], '}']);
25
23
  assert.deepStrictEqual(inspect(parser('{{ }}')), [['<span class="template">{{ }}</span>'], '']);
26
24
  assert.deepStrictEqual(inspect(parser('{{ a }}')), [['<span class="template">{{ a }}</span>'], '']);
25
+ assert.deepStrictEqual(inspect(parser('{{\n}}')), [['<span class="template">{{<br>}}</span>'], '']);
26
+ assert.deepStrictEqual(inspect(parser('{{\\\n}}')), [['<span class="template">{{\\<br>}}</span>'], '']);
27
27
  assert.deepStrictEqual(inspect(parser('{{a}}')), [['<span class="template">{{a}}</span>'], '']);
28
28
  assert.deepStrictEqual(inspect(parser('{{a b}}')), [['<span class="template">{{a b}}</span>'], '']);
29
29
  assert.deepStrictEqual(inspect(parser('{{\\}}}')), [['<span class="template">{{\\}}}</span>'], '']);
@@ -2,18 +2,17 @@ import { TemplateParser } from '../inline';
2
2
  import { Recursion, Backtrack } from '../context';
3
3
  import { union, some, recursion, precedence, surround, lazy } from '../../combinator';
4
4
  import { escsource, str } from '../source';
5
- import { html } from 'typed-dom/dom';
5
+ import { unshift, push } from 'spica/array';
6
+ import { html, defrag } from 'typed-dom/dom';
6
7
 
7
8
  export const template: TemplateParser = lazy(() => surround(
8
- '{{',
9
+ str('{{'),
9
10
  precedence(1,
10
11
  some(union([bracket, escsource]), '}')),
11
- '}}',
12
+ str('}}'),
12
13
  true,
13
- ([, ns = []], rest, context) =>
14
- context.linebreak === undefined
15
- ? [[html('span', { class: 'template' }, `{{${ns.join('')}}}`)], rest]
16
- : undefined,
14
+ ([as, bs = [], cs], rest) =>
15
+ [[html('span', { class: 'template' }, defrag(push(unshift(as, bs), cs)))], rest],
17
16
  undefined,
18
17
  [3 | Backtrack.doublebracket, 1 | Backtrack.bracket]));
19
18
 
@@ -139,6 +139,7 @@ describe('Unit: parser/inline', () => {
139
139
  assert.deepStrictEqual(inspect(parser('(([[a] ]))')), [['<sup class="annotation"><span>[[a] ]</span></sup>'], '']);
140
140
  assert.deepStrictEqual(inspect(parser('(([["*(*"] ]))')), [['<sup class="annotation"><span>[["*(*"] ]</span></sup>'], '']);
141
141
  assert.deepStrictEqual(inspect(parser('(([:a\n]')), [['(', '(', '<span class="invalid">a<br></span>'], '']);
142
+ assert.deepStrictEqual(inspect(parser('(({{\n}}')), [['(', '(', '<span class="template">{{<br>}}</span>'], '']);
142
143
  assert.deepStrictEqual(inspect(parser('"((""))')), [['"', '(', '(', '"', '"', ')', ')'], '']);
143
144
  assert.deepStrictEqual(inspect(parser('[[[a]]')), [['[', '<sup class="reference"><span>a</span></sup>'], '']);
144
145
  assert.deepStrictEqual(inspect(parser('[[[[a]]')), [['[', '[', '<sup class="reference"><span>a</span></sup>'], '']);
@@ -20,13 +20,13 @@ describe('Unit: parser/source/escsource', () => {
20
20
  assert.deepStrictEqual(inspect(parser(' ')), [[' '], '']);
21
21
  assert.deepStrictEqual(inspect(parser(' ')), [[' ', ' '], '']);
22
22
  assert.deepStrictEqual(inspect(parser(' ')), [[' ', ' '], '']);
23
- //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '\n'], '']);
24
- //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
25
- //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
23
+ assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '<br>'], '']);
24
+ assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '<br>'], '']);
25
+ assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '<br>'], '']);
26
26
  });
27
27
 
28
28
  it('linebreak', () => {
29
- //assert.deepStrictEqual(inspect(parser('\n\n')), [['\n', '\n'], '']);
29
+ assert.deepStrictEqual(inspect(parser('\n\n')), [['<br>', '<br>'], '']);
30
30
  });
31
31
 
32
32
  it('\\', () => {
@@ -39,7 +39,7 @@ describe('Unit: parser/source/escsource', () => {
39
39
  assert.deepStrictEqual(inspect(parser('\\a')), [['\\a'], '']);
40
40
  assert.deepStrictEqual(inspect(parser('\\$')), [['\\$'], '']);
41
41
  assert.deepStrictEqual(inspect(parser('\\ ')), [['\\ '], '']);
42
- //assert.deepStrictEqual(inspect(parser('\\\n')), [['\\', '\n'], '']);
42
+ assert.deepStrictEqual(inspect(parser('\\\n')), [['\\', '<br>'], '']);
43
43
  });
44
44
 
45
45
  });
@@ -2,6 +2,7 @@ import { EscapableSourceParser } from '../source';
2
2
  import { Command } from '../context';
3
3
  import { consume } from '../../combinator';
4
4
  import { nonWhitespace } from './text';
5
+ import { html } from 'typed-dom/dom';
5
6
 
6
7
  const delimiter = /[\s\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]/;
7
8
 
@@ -20,7 +21,6 @@ export const escsource: EscapableSourceParser = ({ source, context }) => {
20
21
  consume(-1, context);
21
22
  return [[], source.slice(1)];
22
23
  case Command.Escape:
23
- assert(false);
24
24
  consume(1, context);
25
25
  return [[source.slice(1, 2)], source.slice(2)];
26
26
  case '\\':
@@ -35,7 +35,7 @@ export const escsource: EscapableSourceParser = ({ source, context }) => {
35
35
  }
36
36
  case '\n':
37
37
  context.linebreak ??= source.length;
38
- return [[source[0]], source.slice(1)];
38
+ return [[html('br')], source.slice(1)];
39
39
  default:
40
40
  assert(source[0] !== '\n');
41
41
  const b = source[0].trimStart() === '';
@@ -20,13 +20,13 @@ describe('Unit: parser/source/unescapable', () => {
20
20
  assert.deepStrictEqual(inspect(parser(' ')), [[' '], '']);
21
21
  assert.deepStrictEqual(inspect(parser(' ')), [[' ', ' '], '']);
22
22
  assert.deepStrictEqual(inspect(parser(' ')), [[' ', ' '], '']);
23
- //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '\n'], '']);
24
- //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
25
- //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
23
+ assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '<br>'], '']);
24
+ assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '<br>'], '']);
25
+ assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '<br>'], '']);
26
26
  });
27
27
 
28
28
  it('linebreak', () => {
29
- //assert.deepStrictEqual(inspect(parser('\n\n')), [['\n', '\n'], '']);
29
+ assert.deepStrictEqual(inspect(parser('\n\n')), [['<br>', '<br>'], '']);
30
30
  });
31
31
 
32
32
  it('\\', () => {
@@ -39,7 +39,7 @@ describe('Unit: parser/source/unescapable', () => {
39
39
  assert.deepStrictEqual(inspect(parser('\\a')), [['\\', 'a'], '']);
40
40
  assert.deepStrictEqual(inspect(parser('\\`')), [['\\', '`'], '']);
41
41
  assert.deepStrictEqual(inspect(parser('\\ ')), [['\\', ' '], '']);
42
- //assert.deepStrictEqual(inspect(parser('\\\n')), [['\\', '\n'], '']);
42
+ assert.deepStrictEqual(inspect(parser('\\\n')), [['\\', '<br>'], '']);
43
43
  });
44
44
 
45
45
  });
@@ -2,6 +2,7 @@ import { UnescapableSourceParser } from '../source';
2
2
  import { Command } from '../context';
3
3
  import { consume } from '../../combinator';
4
4
  import { delimiter, nonWhitespace, nonAlphanumeric, isAlphanumeric } from './text';
5
+ import { html } from 'typed-dom/dom';
5
6
 
6
7
  export const unescsource: UnescapableSourceParser = ({ source, context }) => {
7
8
  if (source === '') return;
@@ -18,12 +19,11 @@ export const unescsource: UnescapableSourceParser = ({ source, context }) => {
18
19
  consume(-1, context);
19
20
  return [[], source.slice(1)];
20
21
  case Command.Escape:
21
- assert(false);
22
22
  consume(1, context);
23
23
  return [[source.slice(1, 2)], source.slice(2)];
24
24
  case '\n':
25
25
  context.linebreak ??= source.length;
26
- return [[source[0]], source.slice(1)];
26
+ return [[html('br')], source.slice(1)];
27
27
  default:
28
28
  assert(source[0] !== '\n');
29
29
  const b = source[0].trimStart() === '';
@@ -39,9 +39,9 @@ describe('Unit: renderer/render/media', () => {
39
39
  });
40
40
 
41
41
  it('image', () => {
42
- assert(media(location.href, parse('!{/ 4x3}').querySelector('img')!, {}, caches.media)!.matches(`[src="/"][alt=""][width="4"][height="3"]:not([aspect-ratio])`));
43
- assert(media(location.href, parse('!{/ 4:3}').querySelector('img')!, {}, caches.media)!.matches(`[src="/"][alt=""][aspect-ratio="4/3"]:not([width]):not([height])`));
44
- assert(media(location.href, parse('!{/ 4x3}').querySelector('img')!, {}, caches.media)!.matches(`[src="/"][alt=""][width="4"][height="3"]:not([aspect-ratio])`));
42
+ assert(media(location.href, parse('!{/ 4x3}').querySelector('img')!, {}, caches.media)!.matches(`[src="/"][alt="/"][width="4"][height="3"]:not([aspect-ratio])`));
43
+ assert(media(location.href, parse('!{/ 4:3}').querySelector('img')!, {}, caches.media)!.matches(`[src="/"][alt="/"][aspect-ratio="4/3"]:not([width]):not([height])`));
44
+ assert(media(location.href, parse('!{/ 4x3}').querySelector('img')!, {}, caches.media)!.matches(`[src="/"][alt="/"][width="4"][height="3"]:not([aspect-ratio])`));
45
45
  });
46
46
 
47
47
  });