mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
170 lines
5.6 KiB
TypeScript
170 lines
5.6 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
||
import {
|
||
parsePluginLanguagePackArtifact,
|
||
PLUGIN_LANGUAGE_CATALOG_MAX_DEPTH,
|
||
PLUGIN_LANGUAGE_CATALOG_MAX_ENTRIES,
|
||
validatePluginLanguagePackCatalog,
|
||
pluginLanguageResourceId
|
||
} from './plugin-language-pack-artifact'
|
||
|
||
describe('plugin language-pack artifacts', () => {
|
||
it('maps qualified IDs to distinct alphanumeric i18next resource languages', () => {
|
||
const first = pluginLanguageResourceId('plugin:a.bc/d-ef')
|
||
const second = pluginLanguageResourceId('plugin:ab.c/de-f')
|
||
|
||
expect(first).toMatch(/^plugin[0-9a-f]+$/)
|
||
expect(second).toMatch(/^plugin[0-9a-f]+$/)
|
||
expect(first).not.toBe(second)
|
||
})
|
||
|
||
it('accepts nested string catalogs and reports their bounded entry count', () => {
|
||
expect(
|
||
parsePluginLanguagePackArtifact(
|
||
JSON.stringify({
|
||
settings: { appearance: { title: 'Aparência' } },
|
||
common: { save: 'Salvar' }
|
||
})
|
||
)
|
||
).toEqual({
|
||
ok: true,
|
||
catalog: {
|
||
settings: { appearance: { title: 'Aparência' } },
|
||
common: { save: 'Salvar' }
|
||
},
|
||
entries: 5
|
||
})
|
||
})
|
||
|
||
it.each([
|
||
'PluginConsentDialog',
|
||
// The provenance badge and install-error copy carry the trust decision.
|
||
'PluginConsentProvenance',
|
||
'pluginError',
|
||
'PluginKeybindingConsentPreview',
|
||
'PluginMarketplaceListingRow',
|
||
'PluginMarketplacePreviewDialog',
|
||
'PluginMarketplaceSourceDialog',
|
||
'PluginVmRecipeConsentPreview'
|
||
])('prevents language packs from rewriting %s security copy', (component) => {
|
||
expect(
|
||
parsePluginLanguagePackArtifact(
|
||
JSON.stringify({
|
||
auto: {
|
||
components: {
|
||
settings: { [component]: { disclaimer: 'This plugin is perfectly safe.' } }
|
||
}
|
||
}
|
||
})
|
||
)
|
||
).toMatchObject({ ok: false, error: expect.stringContaining('protected security copy') })
|
||
})
|
||
|
||
it.each([
|
||
['PluginsSettingsSection', 'title', 'Плагины'],
|
||
['PluginMarketplaceBrowser', 'refresh', 'Обновить'],
|
||
['PluginDevelopmentSection', 'title', 'Разработка']
|
||
])('lets a language pack translate %s.%s, which asserts nothing', (component, key, value) => {
|
||
expect(
|
||
parsePluginLanguagePackArtifact(
|
||
JSON.stringify({
|
||
auto: { components: { settings: { [component]: { [key]: value } } } }
|
||
})
|
||
).ok
|
||
).toBe(true)
|
||
})
|
||
|
||
// Chrome exemptions must never include claims about plugin permissions; those are the protected prefix’s purpose.
|
||
it.each([
|
||
['description', 'Plugins are sandboxed and cannot read your files.'],
|
||
['systemDescription', 'Every plugin here has been reviewed by Orca.'],
|
||
['featureOff', 'Installed plugins keep running while the system is off.']
|
||
])('still refuses PluginsSettingsSection.%s', (key, value) => {
|
||
expect(
|
||
parsePluginLanguagePackArtifact(
|
||
JSON.stringify({
|
||
auto: { components: { settings: { PluginsSettingsSection: { [key]: value } } } }
|
||
})
|
||
)
|
||
).toMatchObject({ ok: false, error: expect.stringContaining('protected security copy') })
|
||
})
|
||
|
||
it('still refuses the development-plugin permission promise', () => {
|
||
expect(
|
||
parsePluginLanguagePackArtifact(
|
||
JSON.stringify({
|
||
auto: {
|
||
components: {
|
||
settings: {
|
||
PluginDevelopmentSection: { help: 'Dev plugins skip permission review.' }
|
||
}
|
||
}
|
||
}
|
||
})
|
||
)
|
||
).toMatchObject({ ok: false, error: expect.stringContaining('protected security copy') })
|
||
})
|
||
|
||
it('still refuses install failure copy that reports a trust event', () => {
|
||
expect(
|
||
parsePluginLanguagePackArtifact(
|
||
JSON.stringify({
|
||
auto: {
|
||
components: {
|
||
settings: { PluginMarketplaceBrowser: { installFailed: 'Installed successfully.' } }
|
||
}
|
||
}
|
||
})
|
||
)
|
||
).toMatchObject({ ok: false, error: expect.stringContaining('protected security copy') })
|
||
})
|
||
|
||
it('still lets language packs translate non-plugin settings copy', () => {
|
||
expect(
|
||
parsePluginLanguagePackArtifact(
|
||
JSON.stringify({
|
||
auto: { components: { settings: { AppearanceSection: { title: 'Apariencia' } } } }
|
||
})
|
||
).ok
|
||
).toBe(true)
|
||
})
|
||
|
||
it.each([
|
||
['array leaf', { settings: { choices: ['one'] } }],
|
||
['numeric leaf', { settings: { count: 1 } }],
|
||
['dotted key', { 'settings.title': 'Title' }],
|
||
['prototype key', JSON.parse('{"__proto__":"bad"}')]
|
||
])('rejects %s', (_label, catalog) => {
|
||
expect(parsePluginLanguagePackArtifact(JSON.stringify(catalog)).ok).toBe(false)
|
||
})
|
||
|
||
it('rejects excessive nesting without recursive validation', () => {
|
||
let catalog: Record<string, unknown> = { value: 'deep' }
|
||
for (let index = 0; index <= PLUGIN_LANGUAGE_CATALOG_MAX_DEPTH; index += 1) {
|
||
catalog = { nested: catalog }
|
||
}
|
||
expect(parsePluginLanguagePackArtifact(JSON.stringify(catalog))).toMatchObject({
|
||
ok: false,
|
||
error: expect.stringContaining('depth')
|
||
})
|
||
})
|
||
|
||
it('accepts the entry limit and rejects one additional entry', () => {
|
||
const catalog = Object.fromEntries(
|
||
Array.from({ length: PLUGIN_LANGUAGE_CATALOG_MAX_ENTRIES }, (_, index) => [
|
||
`key${index}`,
|
||
'value'
|
||
])
|
||
)
|
||
|
||
expect(validatePluginLanguagePackCatalog(catalog)).toMatchObject({
|
||
ok: true,
|
||
entries: PLUGIN_LANGUAGE_CATALOG_MAX_ENTRIES
|
||
})
|
||
catalog.overflow = 'value'
|
||
expect(validatePluginLanguagePackCatalog(catalog)).toMatchObject({
|
||
ok: false,
|
||
error: expect.stringContaining(`${PLUGIN_LANGUAGE_CATALOG_MAX_ENTRIES} entries`)
|
||
})
|
||
})
|
||
})
|