{"id":15568528,"url":"https://github.com/cetanu/sender_policy_flattener","last_synced_at":"2025-10-11T23:08:43.553Z","repository":{"id":10431112,"uuid":"65709894","full_name":"cetanu/sender_policy_flattener","owner":"cetanu","description":"Compact large SPF chains into flat blocks of IP addresses","archived":false,"fork":false,"pushed_at":"2025-09-27T10:22:27.000Z","size":155,"stargazers_count":39,"open_issues_count":6,"forks_count":19,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-11T23:08:42.618Z","etag":null,"topics":["dns","email","sender-policy-framework","spf","spf-record"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cetanu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2016-08-15T06:30:08.000Z","updated_at":"2025-09-27T10:22:30.000Z","dependencies_parsed_at":"2024-10-26T21:12:49.411Z","dependency_job_id":"c734e3b2-0a35-49f1-a8a3-b9ec33fb1e40","html_url":"https://github.com/cetanu/sender_policy_flattener","commit_stats":{"total_commits":136,"total_committers":5,"mean_commits":27.2,"dds":0.3382352941176471,"last_synced_commit":"ef40bfd05676b245a50575ffa806950e006c4d42"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/cetanu/sender_policy_flattener","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cetanu%2Fsender_policy_flattener","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cetanu%2Fsender_policy_flattener/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cetanu%2Fsender_policy_flattener/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cetanu%2Fsender_policy_flattener/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cetanu","download_url":"https://codeload.github.com/cetanu/sender_policy_flattener/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cetanu%2Fsender_policy_flattener/sbom","scorecard":{"id":271615,"data":{"date":"2025-08-11","repo":{"name":"github.com/cetanu/sender_policy_flattener","commit":"ef40bfd05676b245a50575ffa806950e006c4d42"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Code-Review","score":1,"reason":"Found 3/20 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/cetanu/sender_policy_flattener/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/cetanu/sender_policy_flattener/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/cetanu/sender_policy_flattener/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/cetanu/sender_policy_flattener/test.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/publish.yml:20","Warn: pipCommand not pinned by hash: .github/workflows/publish.yml:21","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:20","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:21","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   4 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/publish.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":6,"reason":"4 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2024-48 / GHSA-fj7x-q9j7-g6q6","Warn: Project is vulnerable to: GHSA-3rq5-2g8h-59hc","Warn: Project is vulnerable to: PYSEC-2022-42969","Warn: Project is vulnerable to: GHSA-jfmj-5v4g-7637"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 15 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T13:32:52.300Z","repository_id":10431112,"created_at":"2025-08-17T13:32:52.300Z","updated_at":"2025-08-17T13:32:52.300Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279009349,"owners_count":26084578,"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","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["dns","email","sender-policy-framework","spf","spf-record"],"created_at":"2024-10-02T17:16:38.858Z","updated_at":"2025-10-11T23:08:43.511Z","avatar_url":"https://github.com/cetanu.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"sender policy flattener\n=======================\nWe had a problem in our organisation that caused our SPF records to become invalid:\n\nWhen customers computers were querying our SPF records, there were more than 10 lookups required after following all of the ``include:`` remarks.\n\nSolution? Query them ourselves, and create a much more condense list of SPF records.\n\n#### But wait... What if the downstream records change?\n\nPart of what the script does is that it creates a JSON file that keeps track of the last list of IP Addresses that your combination of SPF records had.\n\nWhen the hashsum of your IP Addresses changes, it will send out an email (or just dump HTML if it can't find an email server) with a handy diff \u0026 BIND format for viewing what has changed, and promptly updating it.\n\nYou could theoretically extract the flat IP records from the resulting JSON file and automatically update your DNS configuration with it.\n\nInstallation\n--------------------\n\n#### via git clone\n\nClone this repo and run\n\n```shell\npip install poetry\npoetry install\n```\n\n\n#### via pip\n\n```shell\npip install sender_policy_flattener\n```\n\n\nUsage\n----------------\n\n```\nusage: spflat [-h] [-c CONFIG] [-r RESOLVERS] [-e MAILSERVER] [-t TOADDR]\n              [-f FROMADDR] [-s SUBJECT] [-D SENDING_DOMAIN] [-d DOMAINS]\n              [-o OUTPUT]\n\nA script that crawls and compacts SPF records into IP networks. This helps to\navoid exceeding the DNS lookup limit of the Sender Policy Framework (SPF)\nhttps://tools.ietf.org/html/rfc7208#section-4.6.4\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -c CONFIG, --config CONFIG\n                        Name/path of JSON configuration file\n  -r RESOLVERS, --resolvers RESOLVERS\n                        Comma separated DNS servers to be used\n  -e MAILSERVER, -mailserver MAILSERVER\n                        Server to use for mailing alerts\n  -t TOADDR, -to TOADDR\n                        Recipient address for email alert\n  -f FROMADDR, -from FROMADDR\n                        Sending address for email alert\n  -s SUBJECT, -subject SUBJECT\n                        Subject string, must contain {zone}\n  -D SENDING_DOMAIN, --sending-domain SENDING_DOMAIN\n                        The domain which emails are being sent from\n  -d DOMAINS, --domains DOMAINS\n                        Comma separated domain:rrtype to flatten to IP\n                        addresses. Imagine these are your SPF include\n                        statements.\n  -o OUTPUT, --output OUTPUT\n                        Name/path of output file\n```\n\nExample\n\n```shell\nspflat --resolvers 8.8.8.8,8.8.4.4 \\\n    --to me@mydomain.com \\\n    --from admin@mydomain.com \\\n    --subject 'SPF for {zone} has changed!' \\\n    --domains gmail.com:txt,sendgrid.com:txt,yahoo.com:a \\\n    --sending-domain mydomain.com\n```\nor\n\n```shell\nspflat --config spf.json\n```\nYou can specify a config file, or you can specify all of the optional arguments from the command line.\n\nI've provided a ``settings.json`` file with an example configuration file.\n\n\nSupported Python versions\n-------------------------\nSee the latest result of the build: https://github.com/cetanu/sender_policy_flattener/actions\n\n\n3rd party dependencies\n----------------------\n* netaddr\n* dnspython\n\n\nExample email format\n--------------------\n\u003cimg src='https://raw.githubusercontent.com/cetanu/sender_policy_flattener/master/example/email_example.png' alt='example screenshot'\u003e\u003c/img\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcetanu%2Fsender_policy_flattener","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcetanu%2Fsender_policy_flattener","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcetanu%2Fsender_policy_flattener/lists"}