From 6dbc20f87bc180c8a310dd8f03a5e4ce9cb50379 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 3 Feb 2025 14:00:58 +0700 Subject: [PATCH 1/2] feat(developer): verify package version number format in kmc-package Ensures that version number field is not blank, and is a recognized format matching the pattern 'number[.number[.number]]'. Each number component should be an integer, without leading zeroes. The format chosen is more restrictive than semver, because much of our existing infrastructure assumes this format anyway. This does lead to a mismatch in version number format for LDML keyboards, which do support semver according to the spec, so this may need to be revisited in the future to allow for full semver formats. Fixes: #13067 --- .../src/compiler/package-compiler-messages.ts | 7 ++++ .../src/compiler/package-version-validator.ts | 13 +++++++ ...package_version_is_unrecognized_format.kps | 32 +++++++++++++++++ .../src/kmc-package/test/messages.tests.ts | 5 +++ .../src/kmc-package/test/versioning.tests.ts | 34 +++++++++++++++++-- 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 developer/src/kmc-package/test/fixtures/invalid/warn_package_version_is_unrecognized_format.kps diff --git a/developer/src/kmc-package/src/compiler/package-compiler-messages.ts b/developer/src/kmc-package/src/compiler/package-compiler-messages.ts index 6545b7d9639..ca23b2c3768 100644 --- a/developer/src/kmc-package/src/compiler/package-compiler-messages.ts +++ b/developer/src/kmc-package/src/compiler/package-compiler-messages.ts @@ -184,5 +184,12 @@ export class PackageCompilerMessages { static Warn_VisualKeyboardFileIsInvalid = (o:{filename: string}) => m( this.WARN_VisualKeyboardFileIsInvalid, `Visual keyboard file '${def(o.filename)}' is invalid.` ); + + static WARN_PackageVersionIsUnrecognizedFormat = SevWarn | 0x0027; + static Warn_PackageVersionIsUnrecognizedFormat = (o:{version: string}) => m( + this.WARN_PackageVersionIsUnrecognizedFormat, `Package version '${def(o.version)}' has an unrecognized format.`, + `The format for version numbers should be number[.number[.number]]. Each + number component should be an integer, without leading zeroes.` + ); } diff --git a/developer/src/kmc-package/src/compiler/package-version-validator.ts b/developer/src/kmc-package/src/compiler/package-version-validator.ts index b20e086aec7..b6145038299 100644 --- a/developer/src/kmc-package/src/compiler/package-version-validator.ts +++ b/developer/src/kmc-package/src/compiler/package-version-validator.ts @@ -91,6 +91,11 @@ export class PackageVersionValidator { kmp.info.version = {description: kmp.keyboards[0].version}; } + if(result && !isValidVersionNumber(kmp.info.version.description)) { + this.callbacks.reportMessage(PackageCompilerMessages.Warn_PackageVersionIsUnrecognizedFormat({version: kmp.info.version.description})); + return false; + } + return result; } @@ -109,3 +114,11 @@ export class PackageVersionValidator { return true; } } + +function isValidVersionNumber(version: string) { + return /^[0-9]+(\.(0|[1-9]+[0-9]*)){0,2}$/.test(version); +} + +export const unitTestEndpoints = { + isValidVersionNumber, +}; diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_package_version_is_unrecognized_format.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_package_version_is_unrecognized_format.kps new file mode 100644 index 00000000000..30e310bc6e1 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_package_version_is_unrecognized_format.kps @@ -0,0 +1,32 @@ + + + + 15.0.266.0 + 7.0 + + + + warn_package_version_is_unrecognized_format + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0-invalid + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/messages.tests.ts b/developer/src/kmc-package/test/messages.tests.ts index ba5ec56b08b..fe41bbb522a 100644 --- a/developer/src/kmc-package/test/messages.tests.ts +++ b/developer/src/kmc-package/test/messages.tests.ts @@ -241,4 +241,9 @@ describe('PackageCompilerMessages', function () { await testForMessage(this, ['invalid', 'error_package_file_has_empty_version.kps'], PackageCompilerMessages.ERROR_PackageFileHasEmptyVersion); }); + + it('should generate Warn_PackageVersionIsUnrecognizedFormat if package version field is not a valid format', async function() { + await testForMessage(this, ['invalid', 'warn_package_version_is_unrecognized_format.kps'], + PackageCompilerMessages.WARN_PackageVersionIsUnrecognizedFormat); + }); }); diff --git a/developer/src/kmc-package/test/versioning.tests.ts b/developer/src/kmc-package/test/versioning.tests.ts index 98d00b853ce..cbd210750a3 100644 --- a/developer/src/kmc-package/test/versioning.tests.ts +++ b/developer/src/kmc-package/test/versioning.tests.ts @@ -1,9 +1,10 @@ import 'mocha'; import { assert } from 'chai'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; -import { makePathToFixture } from './helpers/index.js'; -import { KmpCompiler } from '../src/compiler/kmp-compiler.js'; import { KmpJsonFile } from '@keymanapp/common-types'; +import { KmpCompiler } from '../src/compiler/kmp-compiler.js'; +import { unitTestEndpoints } from '../src/compiler/package-version-validator.js'; +import { makePathToFixture } from './helpers/index.js'; // This unit test was translated from a Delphi test // Keyman.Test.System.CompilePackageVersioningTest, but note the difference in @@ -41,4 +42,33 @@ describe('package versioning', function () { assert.isTrue(kmpJson !== null); }); } + + it('should validate recognized version numbers', function() { + const goodVersions = [ + '1.0', + '1', + '1.0.2', + '2.2.3', + '9', + '0', + ], badVersions = [ + '0.1m', + '2.3.4.5', + '1.03.4', + 'test', + '1.0-beta', + '', + null, + undefined, + 'xxx', + ]; + + for(const v of goodVersions) { + assert.isTrue(unitTestEndpoints.isValidVersionNumber(v), `'${v}' should be recognized as a valid version number`); + } + + for(const v of badVersions) { + assert.isFalse(unitTestEndpoints.isValidVersionNumber(v), `'${v}' should be recognized as an invalid version number`); + } + }); }); From 706f3996c2c5b685a1f6518a78486e3fd92877b9 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 3 Feb 2025 14:07:38 +0700 Subject: [PATCH 2/2] chore(developer): prevent leading zeroes in initial component of version number --- .../src/kmc-package/src/compiler/package-version-validator.ts | 2 +- developer/src/kmc-package/test/versioning.tests.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/developer/src/kmc-package/src/compiler/package-version-validator.ts b/developer/src/kmc-package/src/compiler/package-version-validator.ts index b6145038299..7250b6bef46 100644 --- a/developer/src/kmc-package/src/compiler/package-version-validator.ts +++ b/developer/src/kmc-package/src/compiler/package-version-validator.ts @@ -116,7 +116,7 @@ export class PackageVersionValidator { } function isValidVersionNumber(version: string) { - return /^[0-9]+(\.(0|[1-9]+[0-9]*)){0,2}$/.test(version); + return /^(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*)){0,2}$/.test(version); } export const unitTestEndpoints = { diff --git a/developer/src/kmc-package/test/versioning.tests.ts b/developer/src/kmc-package/test/versioning.tests.ts index cbd210750a3..adcba30019f 100644 --- a/developer/src/kmc-package/test/versioning.tests.ts +++ b/developer/src/kmc-package/test/versioning.tests.ts @@ -52,6 +52,7 @@ describe('package versioning', function () { '9', '0', ], badVersions = [ + '0001', '0.1m', '2.3.4.5', '1.03.4',