yeknal 1.1.9 → 1.3.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/README.md CHANGED
@@ -39,7 +39,6 @@ Behavior:
39
39
  - Uses GitHub API + raw file download by default.
40
40
  - If GitHub API rate limit is hit, it automatically falls back to `git clone` (Git must be installed).
41
41
  - Top-level folders are included only if they contain `SKILL.md`.
42
- - Excludes `Design`, `Security`, `Security_Raw`, and `SEO`.
43
42
  - Sync targets (if parent folder exists):
44
43
  - Gemini: `~/.gemini/antigravity` or `~/.antigravity`
45
44
  - Codex: `~/.codex`
package/bin/yeknal.js CHANGED
@@ -453,6 +453,7 @@ const SCAN_CONFIG_EXTENSIONS = new Set([
453
453
  const SCAN_ALL_EXTENSIONS = new Set([
454
454
  ...SCAN_SOURCE_EXTENSIONS, ...SCAN_CONFIG_EXTENSIONS,
455
455
  ".env", ".html", ".xml",
456
+ ".pem", ".key", ".crt", ".p12", ".pfx", ".jks", ".pkcs12",
456
457
  ]);
457
458
 
458
459
  // Secret patterns from Security-Master.md Part 1:
@@ -481,6 +482,19 @@ const SECRET_SCAN_EXCLUDE_FILES = new Set([
481
482
  "tsconfig.json", "jsconfig.json",
482
483
  ]);
483
484
 
485
+ // Credential file naming patterns — these must be in .gitignore before creation and deleted after testing
486
+ // Security-Master.md Agent Rules: "Gitignore FIRST, delete after testing"
487
+ const CREDENTIAL_FILENAME_PATTERNS = [
488
+ /^test\.env$/i,
489
+ /^\.env\.(test|ci|staging|prod|production|dev|development)(\.\w+)?$/i,
490
+ /^.*credentials?\.(?:json|yaml|yml|env|conf|cfg|ini)$/i,
491
+ /^.*secrets?\.(?:json|yaml|yml|env|conf|cfg|ini)$/i,
492
+ /^.*keyfile\.(?:json|yaml|yml|pem|key)$/i,
493
+ /^.*service[_-]?account.*\.(?:json|yaml|yml)$/i,
494
+ /^.*auth[_-]?keys?\.(?:json|yaml|yml|env)$/i,
495
+ ];
496
+ const CREDENTIAL_EXT_PATTERNS = /\.(?:pem|key|crt|p12|pfx|jks|pkcs12)$/i;
497
+
484
498
  // Walk directory tree collecting scannable files
485
499
  async function walkProjectFiles(rootDir) {
486
500
  const maxDepth = 12;
@@ -729,6 +743,62 @@ async function checkSecretsAndEnv(projectDir, fileContents) {
729
743
  ));
730
744
  }
731
745
 
746
+ // Check 6: Credential-pattern files covered by .gitignore (6 pts)
747
+ // Security-Master.md Agent Rules: gitignore BEFORE creation, delete after testing
748
+ const credPatternFiles = [];
749
+ for (const [fp] of fileContents) {
750
+ const base = path.basename(fp);
751
+ if (/example|sample|template|placeholder/i.test(base)) continue;
752
+ if (
753
+ CREDENTIAL_FILENAME_PATTERNS.some((p) => p.test(base)) ||
754
+ CREDENTIAL_EXT_PATTERNS.test(base)
755
+ ) {
756
+ credPatternFiles.push(fp);
757
+ }
758
+ }
759
+
760
+ if (credPatternFiles.length === 0) {
761
+ checks.push(checkResult(
762
+ "No credential files outside .gitignore", "Security-Master Agent Rules", 6, 6, "pass",
763
+ "No unprotected credential-pattern files detected",
764
+ ));
765
+ } else {
766
+ const isGitRepo = await isDirectory(path.join(projectDir, ".git"));
767
+ const exposedFiles = [];
768
+
769
+ for (const fp of credPatternFiles) {
770
+ const rel = relPath(projectDir, fp);
771
+ if (isGitRepo) {
772
+ // git check-ignore exits 0 if the file IS ignored, 1 if it is NOT ignored
773
+ const result = await execCommandSafe(`git check-ignore -q "${rel}"`, { cwd: projectDir });
774
+ if (result.error) {
775
+ exposedFiles.push({
776
+ file: rel,
777
+ message: "NOT covered by .gitignore — this file could be pushed to GitHub. Add to .gitignore immediately and delete after testing.",
778
+ });
779
+ }
780
+ } else {
781
+ exposedFiles.push({
782
+ file: rel,
783
+ message: "Credential-pattern file found (not a git repo — ensure it is never committed or deployed)",
784
+ });
785
+ }
786
+ }
787
+
788
+ if (exposedFiles.length === 0) {
789
+ checks.push(checkResult(
790
+ "No credential files outside .gitignore", "Security-Master Agent Rules", 6, 6, "pass",
791
+ `${credPatternFiles.length} credential-pattern file(s) found — all covered by .gitignore`,
792
+ ));
793
+ } else {
794
+ checks.push(checkResult(
795
+ "No credential files outside .gitignore", "Security-Master Agent Rules", 6, 0, "fail",
796
+ `${exposedFiles.length} credential-pattern file(s) are NOT in .gitignore. Risk of being pushed to GitHub.`,
797
+ exposedFiles,
798
+ ));
799
+ }
800
+ }
801
+
732
802
  return { name: "Secrets & Environment", checks };
733
803
  }
734
804
 
@@ -1792,7 +1862,7 @@ function generateSecurityLog(results) {
1792
1862
  lines.push(`Issues: ${results.totalIssues}`);
1793
1863
  lines.push(`Warnings: ${results.totalWarnings}`);
1794
1864
  lines.push("");
1795
- lines.push("Based on: Security-Master.md (yeknal security guidelines)");
1865
+ lines.push("Based on: Security-Master.md + Security/SKILL.md (yeknal security guidelines)");
1796
1866
  lines.push("Reference: https://github.com/tryraisins/MD_Files/blob/main/Security/Security-Master.md");
1797
1867
  lines.push("");
1798
1868
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeknal",
3
- "version": "1.1.9",
3
+ "version": "1.3.0",
4
4
  "description": "CLI to fetch markdown templates and sync AI agent skills",
5
5
  "main": "bin/yeknal.js",
6
6
  "bin": {