{"id":20741106,"url":"https://github.com/robtimus/text-obfuscation","last_synced_at":"2026-02-04T03:08:16.217Z","repository":{"id":65239444,"uuid":"469108636","full_name":"robtimus/text-obfuscation","owner":"robtimus","description":"Provides functionality for obfuscating text","archived":false,"fork":false,"pushed_at":"2025-01-04T14:21:24.000Z","size":458,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-24T02:53:30.563Z","etag":null,"topics":["javascript","obfuscation","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robtimus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-12T14:38:56.000Z","updated_at":"2025-01-04T14:21:28.000Z","dependencies_parsed_at":"2024-01-20T14:23:04.154Z","dependency_job_id":"b576237c-222e-4ae4-b025-579febc577e9","html_url":"https://github.com/robtimus/text-obfuscation","commit_stats":{"total_commits":27,"total_committers":2,"mean_commits":13.5,"dds":0.03703703703703709,"last_synced_commit":"69501f991d6a37983df5aa104d59063ef2fea7e8"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robtimus%2Ftext-obfuscation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robtimus%2Ftext-obfuscation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robtimus%2Ftext-obfuscation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robtimus%2Ftext-obfuscation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robtimus","download_url":"https://codeload.github.com/robtimus/text-obfuscation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250552037,"owners_count":21449162,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","obfuscation","typescript"],"created_at":"2024-11-17T06:33:51.818Z","updated_at":"2026-02-04T03:08:16.211Z","avatar_url":"https://github.com/robtimus.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# text-obfuscation\n[![npm](https://img.shields.io/npm/v/text-obfuscation)](https://www.npmjs.com/package/text-obfuscation)\n[![Build Status](https://github.com/robtimus/text-obfuscation/actions/workflows/build.yml/badge.svg)](https://github.com/robtimus/text-obfuscation/actions/workflows/build.yml)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=robtimus%3Atext-obfuscation\u0026metric=alert_status)](https://sonarcloud.io/summary/overall?id=robtimus%3Atext-obfuscation)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=robtimus%3Atext-obfuscation\u0026metric=coverage)](https://sonarcloud.io/summary/overall?id=robtimus%3Atext-obfuscation)\n[![Known Vulnerabilities](https://snyk.io/test/github/robtimus/text-obfuscation/badge.svg)](https://snyk.io/test/github/robtimus/text-obfuscation)\n\nProvides functionality for obfuscating text. This can be useful for logging information that contains sensitive information.\n\n## Obfuscating strings\n\n### Pre-defined functions\n\nThe following pre-defined functions are provided that all return an immutable obfuscator.\n\n#### obfuscateAll\n\nReplaces all characters with a mask character that defaults to `*`.\n\n```typescript\nconst obfuscator = obfuscateAll();\nconst obfuscated = obfuscator(\"Hello World\");\n// obfuscated is \"***********\"\n```\n\nNote: using this obfuscator still leaks out information about the length of the original text. One of the following two is more secure.\n\n#### obfuscateWithFixedLength\n\nReplaces the entire text with a fixed number of the given mask character that defaults to `*`.\n\n```typescript\nconst obfuscator = obfuscateWithFixedLength(5);\nconst obfuscated = obfuscator(\"Hello World\");\n// obfuscated is \"*****\"\n```\n\n#### obfuscateWithFixedValue\n\nReplaces the entire text with a fixed value.\n\n```typescript\nconst obfuscator = obfuscateWithFixedValue(\"foo\");\nconst obfuscated = obfuscator(\"Hello World\");\n// obfuscated is \"foo\"\n```\n\n#### obfuscatePortion\n\nWhile the above examples are simple, they are not very flexible. Using `obfuscatePortion` you can build obfuscators that obfuscate only specific portions of text. Some examples:\n\n##### Obfuscating all but the last 4 characters\n\nUseful for obfuscating values like credit card numbers.\n\n```typescript\nconst obfuscator = obfuscatePortion({\n  keepAtEnd: 4,\n});\nconst obfuscated = obfuscator(\"1234567890123456\");\n// obfuscated is \"************3456\"\n```\n\nIt’s advised to use `atLeastFromStart`, to make sure that values of fewer than 16 characters are still obfuscated properly:\n\n```typescript\nconst obfuscator = obfuscatePortion({\n  keepAtEnd: 4,\n  atLeastFromStart: 12,\n});\nconst obfuscated = obfuscator(\"1234567890\");\n// obfuscated is \"**********\" and not \"******7890\"\n```\n\n##### Obfuscating only the last 2 characters\n\nUseful for obfuscating values like zip codes, where the first part is not as sensitive as the full zip code:\n\n```typescript\nconst obfuscator = obfuscatePortion({\n  keepAtStart: Number.MAX_VALUE,\n  atLeastFromEnd: 2,\n});\nconst obfuscated = obfuscator(\"SW1A 2AA\");\n// obfuscated is \"SW1A 2**\"\n```\n\nHere, the `keepAtStart` instructs the obfuscator to keep everything; however, `atLeastFromEnd` overrides that partly to ensure that the last two characters are obfuscated regardless of the value specified by `keepAtStart`.\n\n##### Using a fixed length\n\nSimilar to using `obfuscateAll`, by default an obfuscator built using `obfuscatePortion` leaks out the length of the original text. If your text has a variable length, you should consider specifying a fixed total length for the result. The length of the result will then be the same no matter how long the input is:\n\n```typescript\nconst obfuscator = obfuscatePortion({\n  keepAtStart: 2,\n  keepAtEnd: 2,\n  fixedTotalLength: 6,\n});\nlet obfuscated = obfuscator(\"Hello World\");\n// obfuscated is \"He**ld\"\nobfuscated = obfuscator(\"foo\");\n// obfuscated is \"fo**oo\"\n```\n\nNote that if `keepAtStart` and `keepAtEnd` are both specified, parts of the input may be repeated in the result if the input’s length is less than the combined number of characters to keep. This makes it harder to find the original input. For example, if in the example `foo` would be obfuscated into `fo***o` instead, it would be clear that the input was `foo`. Instead, it can now be anything that starts with `fo` and ends with `oo`.\n\n#### obfuscateCustom\n\n`obfuscateCustom` converts any function that takes a string as input and returns a string into an obfuscator.\n\n```typescript\nconst obfuscator = obfuscateCustom(text =\u003e text.toUpperCase());\nconst obfuscated = obfuscator(\"Hello World\");\n// obfuscated is \"HELLO WORLD\"\n```\n\n### obfuscateNone\n\n`obfuscateNone` is not a function that returns an obfuscator, but an immutable obfuscator itself. It can be used as default to prevent checks. For instance:\n\n```typescript\nconst obfuscator = somePossiblyUndefinedObfuscator || obfuscateNone;\nconst obfuscated = obfuscator(\"Hello World\");\n// obfuscated is \"Hello World\" if somePossiblyUndefinedObfuscator was falsy\n```\n\n### Combining obfuscators\n\nSometimes the obfucators in this library alone cannot perform the obfuscation you need. For instance, if you want to obfuscate credit cards, but keep the first and last 4 characters. If the credit cards are all fixed length, `obfuscatePortion` can do just that:\n\n```typescript\nconst obfuscator = obfuscatePortion({\n  keepAtStart: 4,\n  keepAtEnd: 4,\n});\nconst obfuscated = obfuscator(\"1234567890123456\");\n// obfuscated is \"1234********3456\"\n```\n\nHowever, if you attempt to use such an obfuscator on only a part of a credit card, you could end up leaking parts of the credit card that you wanted to obfuscate:\n\n```typescript\nconst incorrectlyObfuscated = obfuscator(\"12345678901234\");\n// incorrectlyObfuscated is \"1234******1234\" where \"1234********34\" would probably be preferred\n```\n\nTo overcome this issue, it’s possible to combine obfuscators. The form is as follows:\n* Specify the first obfuscator, and the input length to which it should be used.\n* Specify any other obfuscators, and the input lengths to which they should be used. Note that each input length should be larger than the previous input length.\n* Specify the obfuscator that will be used for the remainder.\n\nFor instance, for credit card numbers of exactly 16 characters, the above can also be written like this:\n\n```typescript\nconst obfuscator = obfuscateNone.untilLength(4)\n  .afterThat(obfuscateAll()).untilLength(12)\n  .afterThat(obfuscateNone);\n```\n\nWith this chaining, it’s now possible to keep the first and last 4 characters, but with at least 8 characters in between:\n\n```typescript\nconst obfuscator = obfuscateNone.untilLength(4)\n  .afterThat(obfuscatePortion({\n    keepAtEnd: 4,\n    atLeastFromStart: 8,\n  }));\nconst obfuscated = obfuscator(\"12345678901234\");\n// obfuscated is \"1234********34\"\n```\n\n### Splitting text during obfuscation\n\nTo make it easier to create obfuscators for structured text like email addresses, use a `SplitPoint`. Three implementations are provided :\n* `atFirst(s)` splits at the first occurrence of string `s`.\n* `atLast(s)` splits at the last occurrence of string `s`.\n* `atNth(s, occurrence)` splits at the zero-based specified occurrence of string `s`.\n\nFor instance:\n\n```typescript\n// Keep the domain as-is\nconst localPartObfuscator = obfuscatePortion({\n  keepAtStart: 1,\n  keepAtEnd: 1,\n  fixedTotalLength: 8,\n});\nconst domainObfuscator = obfuscateNone;\nconst obfuscator = atFirst(\"@\").splitTo(localPartObfuscator, domainObfuscator);\nconst obfuscated = obfuscator(\"test@example.org\");\n// obfuscated is \"t******t@example.org\"\n```\n\nTo obfuscate the domain except for the TLD, use a nested `SplitPoint`:\n\n```typescript\n// Keep only the TLD of the domain\nconst localPartObfuscator = obfuscatePortion({\n  keepAtStart: 1,\n  keepAtEnd: 1,\n  fixedTotalLength: 8,\n});\nconst domainObfuscator = atLast(\".\").splitTo(obfuscateAll(), obfuscateNone);\nconst obfuscator = atFirst(\"@\").splitTo(localPartObfuscator, domainObfuscator);\nconst obfuscated = obfuscator(\"test@example.org\");\n// obfuscated is \"t******t@*******.org\"\n```\n\n## Obfuscating object properties\n\nUse `newPropertyObfuscator` to create a function that can obfuscate single object properties as well as recursively all properties in an object.\n\nThe simplest form provides an obfucator function for each property to obfuscate:\n\n```typescript\nconst propertyObfuscator = newPropertyObfuscator({\n  password: obfuscateWithFixedLength(3),\n});\nconst obfuscatedPassword = propertyObfuscator(\"password\", \"admin1234\");\n// obfuscatedPassword is \"***\"\nconst obfuscatedUsername = propertyObfuscator(\"username\", \"admin\");\n// obfuscatedUsername is \"admin\"\nconst obfuscatedObject = propertyObfuscator({\n  username: \"admin\",\n  password: \"admin1234\",\n});\n// obfuscatedObject is { username: \"admin\", password: \"***\" }\n```\n\nThis matches property names case sensitively, and will obfuscate any nested object or array using their JSON string representation. This behaviour can be changed in two ways:\n\n1. Per property. Instead of providing an obfuscator function, provide an object instead:\n    ```typescript\n    const propertyObfuscator = newPropertyObfuscator({\n      password: {\n        obfuscate: obfuscateWithFixedLength(3),\n        caseSensitive: false, // defaults to true\n        forObjects: \"exclude\", // defaults to \"obfuscate\"\n        forArrays: \"exclude\", // defaults to \"obfuscate\"\n      }\n    });\n    ```\n2. Using global options:\n    ```typescript\n    const propertyObfuscator = newPropertyObfuscator({\n      password: obfuscateWithFixedLength(3),\n    }, {\n      caseSensitive: false, // defaults to true\n      forObjects: \"exclude\", // defaults to \"obfuscate\"\n      forArrays: \"exclude\", // defaults to \"obfuscate\"\n    });\n    ```\n\nIn both cases, `forObjects` and `forArrays` can take the following values:\n* `\"exclude\"` to not match properties with object or array values; nested properties will be matched separately.\n* `\"obfuscate\"` to obfuscate the entire object or array using its JSON string representation.\n* `\"inherit\"` to obfuscate each nested scalar property value or array element using the given obfuscator.\n* `\"inherit-overridable\"` to obfuscate each nested scalar property value or array element using the given obfuscator; however, if a nested property has its own obfuscator defined this will be used instead.\n\nFinally, in all formats, it's possible to skip obfuscation by using `\"ignore\"` instead of an obfuscator function. This can be useful for skipping entire object trees.\n\n### Customizing obfuscation\n\nObfuscation iterates over the object structure. It will do so as follows:\n* If a property value is an array, the array is iterated over unless the array itself is obfuscated (`forArrays` is `obfuscate`),\n* else if a property value is an object, the _enumerable own properties_ are iterated over unless the object itself is obfuscated (`forObjects` is `obfuscate`),\n* else the property value is considered _scalar_, and either the property value's string representation is obfuscated, or the property value is included as-is.\n\nFor objects, the enumerable own properties may not accurately describe the object. Examples are `Date`, `Set` and `Map`, which have no enumerable own properties and by default result in empty objects (`{}`). Two options can be used to alter handling of objects:\n\n1. Replace values before they are handled. This can be done by setting global option `replacer` to a function that takes the value to potentially replace and the key for the object in the containing object. For array elements the key is the key for the enclosing array. The object in which the value was found is provided as the function's `this` context. If the function returns `undefined` the property will be omitted instead.\n    ```typescript\n    const propertyObfuscator = newPropertyObfuscator({\n      setAsArray: obfuscateWithFixedLength(3),\n      setAsObject: obfuscateWithFixedLength(3),\n    }, {\n      forObjects: \"inherit\", // otherwise setAsObject will be obfuscated to \"***\"\n      forArrays: \"inherit\", // otherwise setAsArray will be obfuscated to \"***\"\n      replacer: (value, key) =\u003e {\n        if (key === \"setAsArray\" \u0026\u0026 value instanceof Set) {\n          return [...value];\n        }\n        if (key === \"removedSet\") {\n          return undefined;\n        }\n        return value;\n      },\n    })\n    const obfuscatedObject = propertyObfuscator({\n      setAsArray: new Set([\"a\", \"b\", \"c\"]),\n      setAsObject: new Set([\"a\", \"b\", \"c\"]),\n      removedSet: new Set([\"a\", \"b\", \"c\"]), // this gets removed because it's replaced with undefined\n    });\n    // obfuscatedObject is { setAsArray: [\"***\", \"***\", \"***\"], setAsObject: {} }\n    ```\n    Note that the replacer can also be used for non-object values like strings or numbers. The only value it will not be called for is `undefined`.\n2. Let objects be treated as scalar instead of as objects. This is already done automatically for instances of `Date`, but it can be done for other object types by setting global option `treatAsScalar` to a function that takes the object to check and the key for the object in the containing object. For array elements the key is the key for the enclosing array. The object in which the object was found is provided as the function's `this` context.\n    ```typescript\n    const propertyObfuscator = newPropertyObfuscator({\n      scalarError: atFirst(\": \").splitTo(obfuscateNone, obfuscateWithFixedLength(3)),\n    }, {\n      treatAsScalar: (o, key) =\u003e key === \"scalarError\" \u0026\u0026 o instanceof Error,\n    })\n    const obfuscatedObject = propertyObfuscator({\n      scalarError: new Error(\"this will be treated as scalar\"),\n      nonScalarError: new Error(\"this will become an empty object\"),\n    });\n    // obfuscatedObject is { scalarError: \"Error: ***\", nonScalarError: {} }\n    ```\n\n\n## Obfuscating HTTP headers\n\nUse `newHeaderObfuscator` to create a function that can obfuscate single HTTP headers (as strings and string arrays) and HTTP header objects. It's much like `newPropertyObfuscator`, but like HTTP headers it's always case insensitive. Unlike `newPropertyObfuscator`, it doesn't support nested objects, and for nested arrays each element is obfuscated separately. It also does not support skipping obfuscation.\n\n```typescript\nconst headerObfuscator = newHeaderObfuscator({\n  Authorization: obfuscateWithFixedLength(3),\n});\nconst obfuscatedAuthorization = headerObfuscator(\"authorization\", \"Bearer someToken\");\n// obfuscatedAuthorization is \"***\"\nconst obfuscatedAuthorizations = headerObfuscator(\"authorization\", [\"Bearer someToken\"]);\n// obfuscatedAuthorization is [\"***\"]\nconst obfuscatedContentType = headerObfuscator(\"Content-Type\", \"application/json\");\n// obfuscatedContentType is \"application/json\"\nconst obfuscatedHeaders = headerObfuscator({\n  authorization: \"Bearer someToken\",\n  \"content-type\": \"application/json\",\n});\n// obfuscatedHeaders is { authorization: \"***\", \"content-type\": \"application/json\" }\n```\n\n`newHeaderObfuscator` is compatible with `http.OutgoingHttpHeaders` and `http.IncomingHttpHeaders`, which makes it useful for HTTP clients and Express applications.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobtimus%2Ftext-obfuscation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobtimus%2Ftext-obfuscation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobtimus%2Ftext-obfuscation/lists"}